Showing posts with label MyFaces. Show all posts
Showing posts with label MyFaces. Show all posts

Tuesday, March 5, 2019

Way to MyFaces 2.3-next

I (and other commiters) spend many hours to prepare the MyFaces codebase for the upcoming version 2.3-next during the last months, which will be MyFaces 4.0 in the future.
JSF (or Jakarta Faces) 3.0 will be 1:1 the same as JSF 2.3, just with the jakarta.* package names.

In 2.3-next, we did BIG BIG cleanup of our codebase and build.
Wee also removed the old JSF EL (javax.faces.el.*) and @ManagedBean (javax.faces.bean.*) implementations.
What does this mean? The API (javax.faces.el.*) and (javax.faces.bean.*) is still there but the implementation of it has been removed partially. @ManagedBean's are now registered as CDI beans, which means that a CDI runtime is a required dependency now.

The biggest clenaup was that we merged back the "shared" and "shared-public" modules to myfaces-impl, as the shared modules are no longer used by other active projects under the MyFaces umbrella.
We also introduced some real integration tests with Arquillian, to test parts of the container integration and AJAX client side. This makes around 1600 unittests!

OK.... Lets compare some numbers!

JAR size:


Mojarra 2.3 MyFaces 2.3 MyFaces 2.3-next

API
1318 kb 1043 kb
IMPL 3495 kb (combined) 2712 kb 2384 kb

Dependencies
241 kb (beanutils)
141 kb (digester)
TOTAL 3495 kb 4412 kb 3427 kb



Performance:
NOTE: this is not a full benchmark, it's only a single case to demonstrate the difference between MyFaces 2.3 and MyFaces 2.3-next, which was inspired from: https://www.oio.de/public/java/studie-jsf-mojarra-myfaces-performance-vergleich.htm
We have to rerun https://github.com/lu4242/performance-comparison-java-web-frameworks later for a real comparison between MyFaces and Mojarra.


Mojarra 2.3 MyFaces 2.3 MyFaces 2.3-next
GET input.xhtml Average: 67 ms Average: 62 ms Average: 43 ms
POST input.xhtml Average: 115 ms Average: 122 ms Average: 108 ms
GET detail.xhtml Average: 119 ms Average: 124 ms Average: 103 ms
POST detail.xhtml Average: 70 ms Average: 54 ms Average: 43 ms
TOTAL Average: 92 ms
Throughput: 198.7/s
Average: 91 ms
Throughput: 209.8/sec
Average: 74 ms
Throughput: 264.2/sec

This means that MyFaces 3.0 is up to ~15% faster as the already very fast MyFaces 2.3 in this case.
We also did some memory finetuning - i just don't have any numbers right now.

Tuesday, November 7, 2017

Increase your JSF application performance - Version 2017

As my last posts are already 5 years old, i think it's time for a new one :D

Environment:


If you would like to use a JavaEE application server, i would go with TomEE.
It's the fastest and smallest application server available and already comes with Apache MyFaces as JSF implementation and Apache OpenWebBeans as CDI implementation.

There are already really good benchmarks available why we should choose MyFaces over Mojarra and OpenWebBeans over Weld:

Understanding JSF 2.0 Performance – Part 1
Understanding JSF 2.0 Performance – Part 2
Understanding JSF 2.0 Performance – Part 3
CDI Performance

So if you just use a plain servlet container like Tomcat or Jetty, i would also use MyFaces and OpenWebBeans.

In the past it was also a good performance boost if you use JUEL over Tomcat's EL implementation - sadly JUEL doesn't implement EL 3.0.

    Configuration:


    • Common settings:
    • <context-param>
          <param-name>javax.faces.PROJECT_STAGE</param-name>
          <param-value>Production</param-value>
      </context-param>
      <context-param>
          <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
          <param-value>-1</param-value>
      </context-param>
      <context-param>
          <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
          <param-value>server</param-value>
      </context-param>
    • Enable ViewPooling: http://lu4242.blogspot.de/2013/12/view-pooling-in-jsf-22-using-apache.html 
    • NOTE: You must also set:
      <context-param>
          <param-name>org.apache.myfaces.CACHE_EL_EXPRESSIONS</param-name>
          <param-value>alwaysRecompile</param-value>
      </context-param>
    • Enable WhiteSpace compression: http://lu4242.blogspot.de/2012/12/html-white-space-compression-for-jsf.html
    • Disable JSP support in MyFaces - this increases the startup performance and EL resolution:
    • <context-param>
          <param-name>org.apache.myfaces.SUPPORT_JSP_AND_FACES_EL</param-name>
          <param-value>false</param-value>
      </context-param>
      <!--
          NOTE: the ExpressionFactory might differ e.g. on Glassfish or Wildfly.
          This parameter is optional since MyFaces 2.3.3.
      -->
      <context-param>
          <param-name>org.apache.myfaces.EXPRESSION_FACTORY</param-name>
          <param-value>org.apache.el.ExpressionFactoryImpl</param-value>
      </context-param> 
    • Disable ManagedBeans support (since 2.3):
      <context-param>
          <param-name>org.apache.myfaces.SUPPORT_MANAGED_BEANS</param-name>
          <param-value>false</param-value>
      </context-param>
    • Reduce saved view states:
      <context-param>
          <param-name>org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION</param-name>
          <param-value>15</param-value>
      </context-param>
      <context-param>
          <param-name>org.apache.myfaces.NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION</param-name>
          <param-value>3</param-value>
      </context-param> 
    • Disable ViewState compression (better performance but more memory usage):
      <context-param>
          <param-name>org.apache.myfaces.COMPRESS_STATE_IN_SESSION</param-name>
          <param-value>false</param-value>
      </context-param>
    • Enable partial submit in PrimeFaces - this reduces the network traffic:
      <context-param>
          <param-name>primefaces.SUBMIT</param-name>
          <param-value>partial</param-value>
      </context-param>
    • Move above the fold scripts to the bottom (end of body). This is a huge improvement of the visible rendering and removes flickering between navigations (since 6.2):
      <context-param>
          <param-name>primefaces.MOVE_SCRIPTS_TO_BOTTOM</param-name>
          <param-value>true</param-value>
      </context-param>
    • Other params to increase performance:
    • <context-param>
          <param-name>org.apache.myfaces.CHECK_ID_PRODUCTION_MODE</param-name>
          <param-value>false</param-value>
      </context-param>
      <context-param>
          <param-name>org.apache.myfaces.EARLY_FLUSH_ENABLED</param-name>
          <param-value>true</param-value>
      </context-param>
    • Use a custom ServletFilter to set the correct expires/cache headers of your resources (images, stylesheets, javascripts). This is only required if you don't use the JSF resource handling for every resource. The JSF impl should already do it correctly for all JSF managed resources.
    • Compress and optimize your Javascripts in your build process. If you use maven, try primefaces-extensions' closure compiler maven plugin.
    • Enable GZIP in your webserver! If it's not supported by your webserver/container, you can still add the GzipResponseFilter from OmniFaces: http://showcase.omnifaces.org/filters/GzipResponseFilter

    Patterns:


    • Correctly define update/render and process/execute! Often this is a huge improvement as many times the whole form is updated instead only a small part. But i also prefer maintainability over performance here.
    • If you don't use ViewScoped beans, it's a good but small improvement to mark the view as stateless via transient=true.
    • Try using HTML over JSF tags.
      • Especially avoid using h:outputText if you don't need the escaping. Just use EL expressions inside your xhtml template.
      • The same applies for some other components like p:outputPanel. Just use a plain div. If you need to make it updateable, you can still use "passtrough elements" (<div jsf:id="...">...</div>)
    • Never put logic in getters because they can be called multiple times - especially for the rendered attribute!
    • Avoid logic (like inline if-statements) in EL expression! It's better to move those logic into the bean. It's faster and often easier to read and maintain.
    • Prefer AJAX over a full postback!
    • If you have many p:outputLabel's on the page and you know that the input field is required or not, it's a good performance improvemet to set the input to required=true or the outputLabel to indicateRequired=true|false. The default value for indicateRequired since 6.2 is auto, which tries to lookup BeanValidation constrains to check if @NotNull|@NotEmpty|@NotBlank are defined.
    • Cache data, which is required multiple times in the same view, in @RequestScoped beans.
    • Avoid missusing @SessionScoped, @ViewScoped and other long living scopes if not really required. 
    • Try to put only small amount of data in such long living beans. Sometimes a small flag or id of an entity is enough information. Often people put many entities in such long living beans. One of my patterns is to split beans into *Controllers (@RequestScoped) and *StateHolders (@ViewScoped, @ViewAccessScoped). *StateHolders are just data containers.

    Friday, December 14, 2012

    Dynamical switching of JSF state saving method

    By default, it's only possible to configure the state saving method (client or server) globally for all views in your application.

    This is sometimes really annoying as server side performs better but needs more memory.
    Also with server side state saving, it's not possible to cache views (if they have actions) because you will get a ViewExpiredException soon.

    So what about using client-side state for some views and using server-side state for all other views?

    Currently this is only possible with MyFaces (since 2.1.9). Mojarra doesn't support this feature.
    I created a specifiation improvement ticket (http://java.net/jira/browse/JAVASERVERFACES_SPEC_PUBLIC-1147) and hopefully this will be supported in Mojarra soon.

    So how can we use this feature?

    To switch the state saving method, we just need to overwrite/wrap the following method: StateManager#isSavingStateInClient(FacesContext).

    In my application i combined this feature with CODI ViewConfig.

    Create a ViewMetaData:
    @Inherited
    @ViewMetaData(override = true)
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface StateSaving {
    
      Method value();
    
      enum Method {
       SERVER,
       CLIENT
      }
    }
    

    Wrap the StateManager:
    public class ExtensionsStateManager extends StateManagerWrapper {
    
      @Override
      public boolean isSavingStateInClient(final FacesContext context) {
        boolean isSavingStateInClient;
    
        final StateSaving stateSaving = getStateSavingAnnotationViaCodiViewConfigResolver(context);
        if (stateSaving == null) {
          isSavingStateInClient = getWrapped().isSavingStateInClient(context);
        } else {
          isSavingStateInClient = stateSaving.value() == StateSaving.Method.CLIENT;
        }
    
        return isSavingStateInClient;
      }
    }
    

    Configure that JSF should use our StateManager:
    <?xml version="1.0" encoding="UTF-8"?>
    <faces-config xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
      version="2.0">
      <application>
        <state-manager>xxx.xxx.ExtensionsStateManager</state-manager>
      </application>
    </faces-config>
    

    Usage:
    @Page(navigation = NavigationMode.REDIRECT)
    public interface Pages extends ViewConfig {
    
      @Page
      @StateSaving(Method.SERVER)
      class ViewA implements Pages { }
     
      @Page
      @StateSaving(Method.CLIENT)
      class View implements Pages { }
    }


    Currently i'm not a member of a library/framework, where i could add this nice feature.
    Maybe i will release my own lib for such stuff someday.

    Tuesday, August 21, 2012

    Increase your JSF application performance (Part 1 - Environment & Configuration)

    • Always use the newest MyFaces version (As you can read here: Blog Entry)
    • If you just need a plain servlet container, use Tomcat instead of Jetty
    • Use JUEL as EL implementation 
      • Add the newest JUEL API + implementation as depedency
      • Configure MyFaces to use JUEL:
      • <context-param>
                <param-name>org.apache.myfaces.EXPRESSION_FACTORY</param-name>
                <param-value>de.odysseus.el.ExpressionFactoryImpl</param-value>
        </context-param>
    • Increase expression cache in JUEL
      • Create src/main/resources/el.properties
      • Add property javax.el.cacheSize with a custom size. The default size is 1000. In my application i use a size of 3000.
    • If you use CDI, consider to use OpenWebBeans as implementation and configure this in your web.xml:
    • <context-param>
          <param-name>org.apache.myfaces.EL_RESOLVER_COMPARATOR</param-name>
          <param-value>org.apache.myfaces.el.unified.OpenWebBeansELResolverComparator</param-value>
      </context-param>
    • Enable MyFaces EL caching as described in MyFaces Wiki
    • Disable JSP support in MyFaces:
    • <context-param>
          <param-name>org.apache.myfaces.SUPPORT_JSP_AND_FACES_EL</param-name>
          <param-value>false</param-value>
      </context-param>
      Attention: If you set this, you need to provide the "org.apache.myfaces.EXPRESSION_FACTORY" parameter.
    • Other params to increase performance:
    • <context-param>
          <param-name>javax.faces.PROJECT_STAGE</param-name>
          <param-value>Production</param-value>
      </context-param>
      <context-param>
          <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
          <param-value>-1</param-value>
      </context-param>
      <context-param>
          <param-name>org.apache.myfaces.CHECK_ID_PRODUCTION_MODE</param-name>
          <param-value>false</param-value>
      </context-param>
      <context-param>
          <param-name>org.apache.myfaces.VIEW_UNIQUE_IDS_CACHE_ENABLED</param-name>
          <param-value>true</param-value>
      </context-param>
      <context-param>
          <param-name>org.apache.myfaces.SAVE_STATE_WITH_VISIT_TREE_ON_PSS</param-name>
          <param-value>false</param-value>
      </context-param>
      
    • Configure state management as described in MyFaces Wiki
    • Use a custom ServletFilter to set the correct expires/cache headers of your resources (images, stylesheets, javascripts)
    • Compress and optimize your Javascripts in your build process. If you use maven, try primefaces-extensions' closure compiler maven plugin