Search code examples
javajsf-2myfaces

How to detect the available MyFace implementation Version at Runtime


The subject title really says it all:

I would like to detect the version of MyFaces at runtime. Is this possible?

It seems there was a discussion about this kind of feature a long time ago, but I can't find anything if it was implemented and the link in there is broken.

MyFaces version 2.0 and above. Websphere 8.0.x


Solution

  • I finally found a solution. Every class has a package object which has properties for getting the specification version, implementation name and implementation version. Of course the data quality in these properties depends on the implementation, but it worked ok enough with the implementations I used so far (the spec version is wrong and a copy of the implementation version but I can live with that).

    /**
     * This method provides a convenient means of determining the JSF
     * Specification version.
     *
     * @return JSF Specification version, e.g. 2.1
     * @since 1.5
     */
    public static String getSpecificationVersion() {
        return FacesContext.getCurrentInstance().getClass().getPackage().getSpecificationVersion();
    }
    
    /**
     * This method provides a convenient means of determining the JSF
     * Implementation version.
     *
     * @return JSF Implementation version, e.g. 2.1.26
     * @since 1.5
     */
    public static String getImplementationVersion() {
        return FacesContext.getCurrentInstance().getClass().getPackage().getImplementationVersion();
    }
    
    /**
     * This method provides a convenient means of determining the JSF
     * Implementation Title.
     *
     * @return JSF implementation title, e.g. Mojarra.
     * @since 1.5
     */
    public static String getImplementationTitle() {
        return FacesContext.getCurrentInstance().getClass().getPackage().getImplementationTitle();
    }
    

    Since I need this information inside a normal servlet I also have to make sure there exists a FacesContext. Fortunatly @BalusC has described how to do this. You just use this class which creates a new JSF LifeCycle if none is present.

    public class FacesUtil {
    
        // Getters -----------------------------------------------------------------------------------
    
        public static FacesContext getFacesContext(
            HttpServletRequest request, HttpServletResponse response)
        {
            // Get current FacesContext.
            FacesContext facesContext = FacesContext.getCurrentInstance();
    
            // Check current FacesContext.
            if (facesContext == null) {
    
                // Create new Lifecycle.
                LifecycleFactory lifecycleFactory = (LifecycleFactory)
                    FactoryFinder.getFactory(FactoryFinder.LIFECYCLE_FACTORY); 
                Lifecycle lifecycle = lifecycleFactory.getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
    
                // Create new FacesContext.
                FacesContextFactory contextFactory  = (FacesContextFactory)
                    FactoryFinder.getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
                facesContext = contextFactory.getFacesContext(
                    request.getSession().getServletContext(), request, response, lifecycle);
    
                // Create new View.
                UIViewRoot view = facesContext.getApplication().getViewHandler().createView(
                    facesContext, "");
                facesContext.setViewRoot(view);                
    
                // Set current FacesContext.
                FacesContextWrapper.setCurrentInstance(facesContext);
            }
    
            return facesContext;
        }
    
        // Helpers -----------------------------------------------------------------------------------
    
        // Wrap the protected FacesContext.setCurrentInstance() in a inner class.
        private static abstract class FacesContextWrapper extends FacesContext {
            protected static void setCurrentInstance(FacesContext facesContext) {
                FacesContext.setCurrentInstance(facesContext);
            }
        }     
    
    }
    

    Finally since we are using Primefaces we get a wrapped Faces Context, which in turn produces version information of Primefaces and not of Myfaces/Mojarra. So instead of the FacesContet.getCurrentInstance we have to check if it is a PrimeFacesContext and if so unwrap it:

    private static Object facesContext() {
        FacesContext context = FacesContext.getCurrentInstance();
        if (context == null)
            return "No Jsf Context";
        if (context instanceof PrimeFacesContext)
            return ((PrimeFacesContext) context).getWrapped();
        return context;
    }