Monday, March 30, 2015

Customizing Blogger

Introduction

I want to start by blaming Markus Eisele for my misadventures. I was looking at his blog and liked the makeover he gave it. I did find the template he was using, but decided that I would write my own. Well I thought this should be an easy thing to do. I severely underestimated the challenge of making a custom template for myself. It turns out that one of the easiest things turns out to be the hardest. Alright Markus you are off the hook... it may just be my bravado, and belief I can build a better mousetrap that led me down the long lonesome road.

Google is well known for having good and sometimes great APIs for their technology. Blogger is an exception to that rule. There is not one clear cut schema for their layout that I can find ironically using Google itself. You figure with all of the templates and bloggers that this would be covered ad nauseum.  It is not though.

Technologies

I was looking for a simple and elegant framework to make my blog sites look professional, and also make them portable. I had the following requirements:
  1. Mature framework
  2. Can be found on a CDN
  3. Easy to use
  4. Simple to implement
  5. Well Documented
  6. Lots of examples
  7. Flexible
  8. Customizable
  9. Response UI
  10. HTML5
  11. Portable
  12. JSF Compatible
  13. Works with NetBeans IDE for Tooling
I looked at a number of frameworks including Foundation, and Bootstrap. I ended up choosing Foundation since it seemed to be easier to use for me. Your milage my vary.

The first thing I wanted to know was what was the minimum required for a template on Blogger. I discovered that are a couple of versions of the template: an HTML 4.01 version (v.1) and an HTML 5 version (v.2) which are somewhat a hybrid mix of XML, and (X)HTML. I published the basic templates on Gist as shown below.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns='http://www.w3.org/1999/xhtml'
xmlns:b='http://www.google.com/2005/gml/b'
xmlns:data='http://www.google.com/2005/gml/data'
xmlns:expr='http://www.google.com/2005/gml/expr'>
<head>
<title><data:blog.pageTitle/></title>
<b:skin>
<![CDATA[
<!-- SKIN CONTENTS -->
]]>
</b:skin>
</head>
<body>
<!-- BODY CONTENTS -->
</body>
</html>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html b:version='2' class='v2' expr:dir='data:blog.languageDirection'
xmlns='http://www.w3.org/1999/xhtml'
xmlns:b='http://www.google.com/2005/gml/b'
xmlns:data='http://www.google.com/2005/gml/data'
xmlns:expr='http://www.google.com/2005/gml/expr'>
<head>
<title><data:blog.pageTitle/></title>
<b:skin>
<![CDATA[
<!-- SKIN CONTENTS -->
]]>
</b:skin>
</head>
<body>
<!-- BODY CONTENTS -->
</body>
</html>
I have a couple of different blogs and found them to be different so I thought I would share my findings.

The next thing I needed to find out was what was the minimal template I would need for using with Foundation. The template below uses a CDN to deliver the required JS/CSS. The template below is the culmination of a lot of work to make it work with the visual tools on Blogger. Remember to backup your existing template before installing mine.

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml'
xmlns:b='http://www.google.com/2005/gml/b'
xmlns:data='http://www.google.com/2005/gml/data'
xmlns:expr='http://www.google.com/2005/gml/expr'
b:version='2' class='v2 no-js' expr:dir='data:blog.languageDirection'>
<head>
<meta content='width=device-width, initial-scale=1.0' name='viewport'/>
<b:include data='blog' name='all-head-content'/>
<title>
<data:blog.pageTitle/>
</title>
<link href='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/css/foundation.min.css' rel='stylesheet'/>
<link href='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/css/normalize.min.css' rel='stylesheet'/>
<link href='https://cdnjs.cloudflare.com/ajax/libs/foundicons/3.0.0/foundation-icons.min.css' rel='stylesheet'/>
<b:skin>
<![CDATA[
<!-- Add Custom CSS Here -->
]]>
</b:skin>
<b:include data='blog' name='google-analytics'/>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/vendor/modernizr.js' type='text/javascript'>
</script>
</head>
<body>
<!-- NAVBAR -->
<b:section class='navbar' id='navbar' maxwidgets='1' showaddelement='no'>
<b:widget id='Navbar1' locked='true' title='Navbar' type='Navbar'></b:widget>
</b:section>
<!-- META DATA -->
<b:if cond='data:blog.pageType == &quot;index&quot;'>
<div itemscope='itemscope' itemtype='http://schema.org/Blog' style='display: none;'>
<meta expr:content='data:blog.title' itemprop='name'/>
<b:if cond='data:blog.metaDescription'>
<meta expr:content='data:blog.metaDescription' itemprop='description'/>
</b:if>
</div>
</b:if>
<!-- HEADER -->
<div class='row'>
<div class='large-12 medium-12 small-12 columns'>
<b:section class='header' id='header' maxwidgets='1' showaddelement='no'>
<b:widget id='Header1' locked='true' title='Test Blog (Header)' type='Header'></b:widget>
</b:section>
</div>
</div>
<!-- MAIN -->
<div class='row'>
<!-- COLUMN 1 - MAIN -->
<div class='large-9 medium-9 small-9 columns'>
<b:section class='main' id='main' showaddelement='yes'>
<b:widget id='Blog1' locked='true' title='Blog Posts' type='Blog'></b:widget>
<b:widget id='PopularPosts1' locked='false' title='Popular Posts' type='PopularPosts'></b:widget>
</b:section>
</div>
<!-- COLUMN 2 - SIDEBAR -->
<div class='large-3 medium-3 small-3 columns'>
<b:section class='sidebar' id='sidebar' showaddelement='yes'>
<b:widget id='BlogArchive1' locked='false' title='Archive' type='BlogArchive'></b:widget>
<b:widget id='CustomSearch1' locked='false' title='Search' type='CustomSearch'></b:widget>
<b:widget id='Label1' locked='false' title='Labels' type='Label'></b:widget>
</b:section>
</div>
</div>
<!-- FOOTER -->
<div class='row'>
<div class='large-12 medium-12 small-12 columns'>
<b:section class='foot' id='footer-3' showaddelement='yes'>
<b:widget id='Attribution1' locked='true' title='' type='Attribution'></b:widget>
</b:section>
</div>
</div>
<!-- CDN JS Required for Foundation -->
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/vendor/jquery.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/vendor/jquery.cookie.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/vendor/placeholder.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/vendor/fastclick.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.abide.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.accordion.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.alert.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.clearing.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.dropdown.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.equalizer.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.interchange.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.joyride.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.magellan.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.offcanvas.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.orbit.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.reveal.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.slider.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.tab.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.tooltip.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.topbar.min.js' type='text/javascript'>
</script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/foundation/5.5.1/js/foundation/foundation.topbar.min.js' type='text/javascript'>
</script>
<script>
$(document).foundation();
</script>
</body>
</html>

Conclusion

I finally have a working blog site using the new template, and will update all of my sites to use it. My personal non-technical blog site was the first to use the new template. It is still a work in progress, but it looks very nice. Take a peek for yourself at John Yeary Blogger site.

I have compiled a list of links that I found helpful in trying to figure out their layouts and tags in the references below.

References

Tags

Template References

Additional References

Friday, March 27, 2015

A Simple Method to invoke @PreDestroy on a Class

I was experimenting with how to invoke a @PreDestroy annotated method in a class. This will approach will work with other annotations as well.
/*
* Copyright 2016 Blue Lotus Software, LLC.
* Copyright 2015 John Yeary <jyeary@bluelotussoftware.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.bluelotussoftware.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Map;
import javax.annotation.PreDestroy;
/**
*
* @author John Yeary <jyeary@bluelotussoftware.com>
* @version 2.0
*/
public class Utils {
/**
* This method reflectively examines the provided object looking for
* {@link PreDestroy} annotations, invokes the first method with the
* annotation found, and returns.
*
* @param object The object to examine.
* @return {@code true} if successful, and {@code false} otherwise.
* @throws SecurityException If a security manager, <i>s</i>, is present and
* any of the following conditions is met:
* <ul>
* <li> the caller's class loader is not the same as the class loader of
* this class and invocation of
* {@link SecurityManager#checkPermissions#checkPermission(java.security.Permission)}
* method with {@code RuntimePermission("accessDeclaredMembers")} denies
* access to the declared methods within this class.</li>
*
* <li> the caller's class loader is not the same as or an ancestor of the
* class loader for the current class and invocation of
* {@link SecurityManager#checkPackageAccess#checkPackageAccess(java.lang.String)}
* denies access to the package of this class.</li>
* </ul>
* @throws IllegalArgumentException if the method is an instance method and
* the specified object argument is not an instance of the class or
* interface declaring the underlying method (or of a subclass or
* implementor thereof); if the number of actual and formal parameters
* differ; if an unwrapping conversion for primitive arguments fails; or if,
* after possible unwrapping, a parameter value cannot be converted to the
* corresponding formal parameter type by a method invocation conversion.
* @throws IllegalAccessException if this {@code Method} object is enforcing
* Java language access control and the underlying method is inaccessible.
* @throws InvocationTargetException if the underlying method throws an
* exception.
*/
public static boolean destroy(final Object object) throws SecurityException,
IllegalArgumentException, IllegalAccessException, InvocationTargetException {
Class clazz = object.getClass();
String methodName = null;
synchronized (object) {
boolean success = false;
try {
Method[] methods = clazz.getDeclaredMethods();
done:// Break for multiple for loops.
for (Method method : methods) {
Annotation[] annotations = method.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
if (annotation instanceof PreDestroy) {
methodName = method.getName();
if (!method.isAccessible()) {
method.setAccessible(true);
}
method.invoke(object, (Object[]) null);
success = true;
break done;
}
}
}
} catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
System.err.println(MessageFormat.format("An exception occurred while trying to process @PreDestroy on : {0}.{1}(). The message follows: {2}", clazz.getName(), methodName, ex.getMessage()));
throw ex;
}
return success;
}
}
/**
*
* This method reflectively examines the provided map looking for objects
* with {@link PreDestroy} annotations, invokes the first method with the
* annotation found, and returns.
*
* @param map A map of objects to examine for {@link PreDestroy}
* annotations.
* @throws SecurityException If a security manager, <i>s</i>, is present and
* any of the following conditions is met:
* <ul>
* <li> the caller's class loader is not the same as the class loader of
* this class and invocation of
* {@link SecurityManager#checkPermissions#checkPermission(java.security.Permission)}
* method with {@code RuntimePermission("accessDeclaredMembers")} denies
* access to the declared methods within this class.</li>
*
* <li> the caller's class loader is not the same as or an ancestor of the
* class loader for the current class and invocation of
* {@link SecurityManager#checkPackageAccess#checkPackageAccess(java.lang.String)}
* denies access to the package of this class.</li>
* </ul>
* @throws IllegalArgumentException if the method is an instance method and
* the specified object argument is not an instance of the class or
* interface declaring the underlying method (or of a subclass or
* implementor thereof); if the number of actual and formal parameters
* differ; if an unwrapping conversion for primitive arguments fails; or if,
* after possible unwrapping, a parameter value cannot be converted to the
* corresponding formal parameter type by a method invocation conversion.
* @throws IllegalAccessException if this {@code Method} object is enforcing
* Java language access control and the underlying method is inaccessible.
* @throws InvocationTargetException if the underlying method throws an
* exception.
* @see Utils#destroy(java.lang.Object)
* @since 2.0
*/
public static void destroy(final Map<String, Object> map) throws SecurityException,
IllegalAccessException, IllegalArgumentException, InvocationTargetException {
if (map != null) {
Map<String, Object> synchronizedMap = Collections.synchronizedMap(map);
if (!synchronizedMap.isEmpty()) {
for (Object o : synchronizedMap.values()) {
destroy(o);
}
}
}
}
}
view raw Utils.java hosted with ❤ by GitHub

Popular Posts