Wednesday, January 23, 2013

PrimeFaces Cookbook published today

PrimeFaces Cookbook was published today. The book writing was a piece of a hard work and I'm happy to finish it.


I would like to thanks all persons who were involved in the entire writing / reviewing process. Especially the people from the Packt Publishing, the PrimeFaces project lead Çağatay Çivici, my co-writer Mert Çalışkan, our reviewer  Andy Bailey and my family. Çağatay wrote a nice post today. Also Andy wrote a post today. I can not add something more :-)

Friday, January 11, 2013

Access resources in Java archives with Virtual File System (cross-application sever solution)

During my work on a custom JSF library I was looking for a way how to read the metadata from composite components in order to create a nice self-documented library. Based on this great article from Ed Burns I implemented a nice documentation framework which extracts all metadata from the VDL. Main idea:
FacesContext fc = FacesContext.getCurrentInstance();
ViewDeclarationLanguage vdl = fc.getApplication().getViewHandler().getViewDeclarationLanguage(fc, "/views/home.xhtml");
Resource ccResource = fc.getApplication().getResourceHandler().createResource(resourceName, libraryName);
BeanInfo metadata = vdl.getComponentMetadata(context, ccResource);
PropertyDescriptor attributes[] = metadata.getPropertyDescriptors();
// read metadata for cc:attributes, cc:clientBehavior, cc:valueHolder, cc:actionSource, cc:facet
...
The createResource method above expects the resource name and library name to access the metadata of a composite component. The library name is known. Assume the composite components are placed in a JAR file under the folder /META-INF/resources/com/foo That means, the library name is com/foo. The problem is to get names of all resources below the com/foo. Resources are XHTML files as composite components. For instance, for this structure
/META-INF/resources/com/foo/mycomponent1.xhtml
/META-INF/resources/com/foo/mycomponent2.xhtml
the resource names would be mycomponent1.xhtml and mycomponent1.xhtml. Without file extensions, they are the same as tag names of composite components. Is there a simple and reliable way to read these names from a WAR archive? Yes, sure, there is a simple method to read resources located in one Java archive from another Java archive. JBoss has Virtual File System (VFS). A good documentation can be found here. Please don't confuse it with Apache Commons VFS. These are different things with perhaps the same goal. In JBoss 6 / 7 application servers, if we read JAR files from the classpath, URLs start with vfs (meaning virtual file system) and not with file as usually. So that JBoss VFS is very handy here because it is exactly for vfs handling. The implemented approach is also working with all other application servers. I tested successful:
  • JBoss 6 / 7
  • Jetty 8
  • GlassFish 3
  • Tomcat 7
  • WebLogic 12
The next method shows the approach. To get an URL for the interested JAR file, we need to know any Java class in this JAR file. This can be a marker interface or any other interface, abstract class as well. Obtained Class object can be passed into the method along with the library name.
public void extractMetadata(Class clazz, String libraryName) throws IOException, URISyntaxException {
  VirtualFile virtualFile;
  Closeable handle = null;
  URL url = clazz.getProtectionDomain().getCodeSource().getLocation();
  String protocol = url.getProtocol();

  if ("vfs".equals(protocol)) {
    URLConnection conn = url.openConnection();
    virtualFile = (VirtualFile) conn.getContent();
  } else if ("file".equals(protocol)) {
    virtualFile = VFS.getChild(url.toURI());

    File archiveFile = virtualFile.getPhysicalFile();
    TempFileProvider provider = TempFileProvider.create("tmp", Executors.newScheduledThreadPool(2));
    handle = VFS.mountZip(archiveFile, virtualFile, provider);
  } else {
    throw new UnsupportedOperationException("Protocol " + protocol + " is not supported");
  }

  List<VirtualFile> files = virtualFile.getChild("/META-INF/resources/" + libraryName).getChildren();
  List<String> resourceNames = new ArrayList<String>(files.size());
  for (VirtualFile ccFile : files) {
    resourceNames.add(ccFile.getName());
  }

  if (handle != null) {
    handle.close();
  }

  FacesContext fc = FacesContext.getCurrentInstance();
  ViewDeclarationLanguage vdl = fc.getApplication().getViewHandler().getViewDeclarationLanguage(fc,
     "/views/home.xhtml");

  for (String resourceName : resourceNames) {
    Resource ccResource = fc.getApplication().getResourceHandler().createResource(resourceName, libraryName);
    BeanInfo metadata = vdl.getComponentMetadata(fc, ccResource);
  
    // extract metadata
    ...
  }
}
As I said, supporting of two URL protocols, vfs and file, was enough for me to get it working on all modern app. servers. The main goal of this method is collecting VirtualFile instances below a certain folder. VirtualFile allows to get other infos such as size, name, path, whether the object is a file or directory, etc. It allows to get children und provides many other traversal operations. For the file protocol, we have to mount the archive file in the Virtual File System (like in UNIX). Once mounted, the archive structure is accessible like a normal file system. The call virtualFile.getChild("/META-INF/resources/" + libraryName).getChildren() returns all children (VirtualFile instances) below our /META-INF/resources/com/foo/. We iterate through and collect resource (file) names.

Monday, January 7, 2013

PrimeFaces Extensions goes social

PrimeFaces Extensions is now more social than ever. The project has been successfully migrated to GitHub. Please visit our new Homepage. You will find there all links, to the showcase, new issue tracker, wiki, daily builds, etc. Please only use the new infrastructure on the GitHub (especially for issues). After the next main release, we will delete the old, deprecated infrastructure on the google code completely.

I would like to thanks Nilesh Mali and Thomas Andraschko that helped me a lot during migration. We could migrate all sub-projects with the whole commits history since the project exists. An overview of existing sub-projects can be found here.

Quick links to the new infrastructure:

We would like to invite everyone to participate in the PrimeFaces Extensions. Please feel free to create issues and feature requests. Feel also free to initiate Pull Requests. We will review and accept all reasonable suggestions and patches. This is a true open source project!