2

I have this page:

<c:forEach begin="0" end="#{tareaController.tipoFuncion.filas}" step="1" var="x">
    <p:row>
        <c:forEach begin="0" end="#{tareaController.tipoFuncion.columnas}" step="1" var="y">
            <c:choose>
                <c:when test="#{tareaController.configuracionEnPosicion(x,y) ne null}">
                    <p:column style="width: 80px" 
                              colspan="#{tareaController.configuracionEnPosicion(x,y).colSpan}"
                              rowspan="#{tareaController.configuracionEnPosicion(x,y).rowSpan}">
                        <p:outputLabel value="#{tareaController.atributoEnPosicion(x,y).labelAtributo}:" 
                                       rendered="#{!tareaController.validar.isBoton(tareaController.atributoEnPosicion(x, y).tipoAtributosOpcionales)}">
                            <c:if test="#{tareaController.configuracionEnPosicion(x,y).esObligatorio}">
                                *
                            </c:if>
                        </p:outputLabel>
                    </p:column>
                    <p:column colspan="#{tareaController.configuracionEnPosicion(x,y).colSpan}"
                              rowspan="#{tareaController.configuracionEnPosicion(x,y).rowSpan}"> 
                        <c:choose>
                            <c:when test="#{tareaController.validar.isSeparador(tareaController.atributoEnPosicion(x, y).tipoAtributosOpcionales)}">
                                <p:separator/>
                            </c:when>
                            <c:when test="#{tareaController.validar.isNumerico(tareaController.atributoEnPosicion(x,y).tipoAtributosOpcionales)}">
                                <pe:inputNumber decimalSeparator="#{tareaController.configuracionEnPosicion(x,y).separadorDecimal}" 
                                                thousandSeparator="#{tareaController.configuracionEnPosicion(x,y).separadorMiles}" 
                                                decimalPlaces="#{tareaController.configuracionEnPosicion(x,y).cantidadDecimales}" 
                                                disabled="#{mFuncion.soloLectura || !tareaController.tareaAceptada || tareaController.tomadaPorOtroUsuario || tareaController.configuracionEnPosicion(x,y).soloLectura}"
                                                maxValue="#{tareaController.configuracionEnPosicion(x,y).valorMaximo}"
                                                rendered="#{!tareaController.configuracionEnPosicion(x,y).ocultarAtributo}"
                                                value="#{tareaController.atributoEnPosicion(x,y).valorAtributoNumerico}" 
                                                style="width: #{tareaController.configuracionEnPosicion(x,y).ancho}px; height: #{tareaController.configuracionEnPosicion(x,y).alto}px;">
                                    <p:ajax global="false" update=":f_tarea:tabView:tab_#{tareaController.indiceTramite}:nombreRepetido, :f_tarea:b_completarT" listener="#{tareaController.validarTarea()}"/>
                                </pe:inputNumber>
                            </c:when>
                            <c:when test="#{tareaController.validar.isBoton(tareaController.atributoEnPosicion(x, y).tipoAtributosOpcionales)}">
                                <p:commandButton value="#{tareaController.atributoEnPosicion(x,y).labelAtributo}" 
                                                 actionListener="#{tareaController.ejecutarWs(tareaController.configuracionEnPosicion(x,y))}"
                                                 update=":f_tarea:tabView:tab_#{tareaController.indiceTramite}:pg_funcion_#{tareaController.indiceTramite}"/>
                            </c:when>
                            <c:when test="#{tareaController.validar.isFecha(tareaController.atributoEnPosicion(x,y).tipoAtributosOpcionales)}">
                                <p:calendar value="#{tareaController.atributoEnPosicion(x,y).valorAtributoFecha}" 
                                            maxdate="#{tareaController.configuracionEnPosicion(x,y).fechaMaxima}" 
                                            mindate="#{tareaController.configuracionEnPosicion(x,y).fechaMinima}"
                                            rendered="#{!tareaController.configuracionEnPosicion(x,y).ocultarAtributo}"
                                            disabled="#{mFuncion.soloLectura || !tareaController.tareaAceptada || tareaController.tomadaPorOtroUsuario || tareaController.configuracionEnPosicion(x,y).soloLectura || tareaController.atributoEnPosicion(x,y).bloquear}"
                                            size="20"
                                            pattern="#{tareaController.configuracionEnPosicion(x,y).formatoFecha}">
                                    <p:ajax global="false" update=":f_tarea:tabView:tab_#{tareaController.indiceTramite}:nombreRepetido, :f_tarea:b_completarT" listener="#{tareaController.validarTarea()}"/>
                                    <p:ajax event="dateSelect" global="false" update=":f_tarea:tabView:tab_#{tareaController.indiceTramite}:nombreRepetido, :f_tarea:b_completarT" listener="#{tareaController.validarTarea()}"/>
                                </p:calendar>
                            </c:when>
                            <c:otherwise test="#{tareaController.validar.isAlfanumerico(tareaController.atributoEnPosicion(x,y).tipoAtributosOpcionales)}">
                                <p:inputTextarea value="#{tareaController.atributoEnPosicion(x,y).valorAtributoAlfanumerico}" 
                                                 disabled="#{mFuncion.soloLectura || !tareaController.tareaAceptada || tareaController.tomadaPorOtroUsuario || tareaController.configuracionEnPosicion(x,y).soloLectura}"
                                                 rendered="#{!tareaController.configuracionEnPosicion(x,y).ocultarAtributo}"
                                                 style="width: #{tareaController.configuracionEnPosicion(x,y).ancho}px; 
                                                 height:  #{tareaController.configuracionEnPosicion(x,y).alto}px; 
                                                 word-break: normal;">
                                    <p:ajax global="false" update=":f_tarea:tabView:tab_#{tareaController.indiceTramite}:nombreRepetido, :f_tarea:b_completarT" listener="#{tareaController.validarTarea()}"/>
                                    <p:ajax event="select" global="false" update=":f_tarea:tabView:tab_#{tareaController.indiceTramite}:nombreRepetido, :f_tarea:b_completarT" listener="#{tareaController.validarTarea()}"/>
                                </p:inputTextarea>
                            </c:otherwise>
                        </c:choose>
                    </p:column>
                </c:when>
                <c:otherwise>
                    <p:column>
                        <h:panelGroup/>
                    </p:column>
                    <p:column>
                        <h:panelGroup/>
                    </p:column>
                </c:otherwise>
            </c:choose>
        </c:forEach>

The idea is that I can configure that page and place the components in the position I want with lots of different configurations. It work pretty fine and people is happy with that, but I'm not. If you check on the page I call the methods configuracionEnPosicion(x,y) and atributoEnPosicion(x,y) like 300 times on every run on the cycle. And these methods call like other 3 methods and do a foreach for find the actual configuration in x,y. Amazingly it doesn't work slow but I feel like that's a lot of overhead that I can avoid.

Before I tried to save the result of configuracionEnPosicion(x,y) and atributoEnPosicion(x,y) in a variable on the client side with their respective getter and setter, but since the JSTL Core tag runs on build time, in rendering time that variable was not updated, resulting in that variable keeping its last value and giving a not desired behavior. My "solution" was the actual one, but my final question is: Is there a way to store the value of that variable so I'd could only keep it alive during build time or something like that? I'd like to call these methods just ONCE per every x and y position, but my knowledges on JSF and Primefaces are not enough to achieve that.

Aditional information:

  • Apache Tomcat 7.0.41.
  • JSF 2.2
  • Primefaces 4.0
  • Mojarra 2.7.2
  • jstl-1.2
Kukeltje
  • 12,223
  • 4
  • 24
  • 47
Yayotrón
  • 1,759
  • 16
  • 27

3 Answers3

3

It looks like task for JSR 107: JCACHE - Java Temporary Caching API. But you should use CDI beans instead of JSF managed beans. This allows you write code like this:

 @CacheResult(cacheName = "configuracionEnPosicion")
 public String configuracionEnPosicion(int x, int y){
    .....
 }

Also it is necessary create META-INF/beans.xml:

 <beans 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/beans_1_0.xsd">
    <interceptors>
         <class>org.jsr107.ri.annotations.cdi.CacheResultInterceptor</class>
         <class>org.jsr107.ri.annotations.cdi.CachePutInterceptor</class>
         <class>org.jsr107.ri.annotations.cdi.CacheRemoveEntryInterceptor</class>
         <class>org.jsr107.ri.annotations.cdi.CacheRemoveAllInterceptor</class>
    </interceptors>
</beans>

Also it is necessary create cache somewhere:

@PostConstruct
public void init() {
    try {
        // Retrieve the system wide cache manager
        CacheManager cacheManager = Caching.getCachingProvider().getCacheManager();
        // Define a named cache with default JCache configuration
        MutableConfiguration<String, String> configuration = new MutableConfiguration<>();
        //specify expiry policy
        configuration.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(Duration.ONE_MINUTE));
        cache = cacheManager.createCache("configuracionEnPosicion", configuration);
    } catch (Exception e) {
        L.error("Can't init cache!", e);
    }
}

And of course you should select jcache provider (ecache, infinispan, ...) and add necessary dependencies to project.

sibnick
  • 3,995
  • 20
  • 20
1

I have EDIT my answer after @BalusC comments

You could precalculate all the values in the backing bean PostConstruct method.

  • Declare a bidimensional array at backing bean level:

    private ReturnedObjectClass[][] precalculatedArray;
    
  • Add the getter method.

  • Initialize it in the PostConstruct method:

    precalculatedArray = new ReturnedObjectClass[x][y];
    
  • Populate it with the objects returned by your function in the PostConstruct method:

    precalculatedArray[i][j] = myBigFunction(i,j);
    

Then you can access the calculated values from your page using:

    value="#{tareaController.precalculatedArray[x][y].propertyOfReturnedObjectClass}"
Javier Haro
  • 1,255
  • 9
  • 14
  • Explicitly putting in request scope is clumsy. Just make sure the bean is in right scope. – BalusC Sep 18 '15 at 14:34
  • @BalusC, could Flash scope be a better alternative? – Javier Haro Sep 18 '15 at 14:50
  • Why? Just put bean in right scope and simply use `#{tareaController.precalculatedArray}`. – BalusC Sep 18 '15 at 14:58
  • Yes, you are right. I was trying to maintain the calculated array as less time as possible in memory, but the right path is give it the same lifetime as the backing bean. I will edit my answer. Thanks. – Javier Haro Sep 18 '15 at 15:18
-1

You could use c:set.

<c:set var="salary" scope="request" value="#{calculatePosition(x,y)}"/>

or maybe you could use varstatus depending on what happens in your fct. (this is the doc for a jsf tag not a jstl tag but it's the same thing).

Ced
  • 15,847
  • 14
  • 87
  • 146
  • [The `` without a `scope` is just an alias](http://stackoverflow.com/q/6434866). OP wants to avoid that the same -apparently expensive- task inside `calculatePosition()` method is re-executed every time for the same arguments. – BalusC Sep 18 '15 at 06:53
  • 2
    So, would it work without re-evaluating, if we used `scope="request"`? – yannicuLar Sep 18 '15 at 11:58
  • 1
    Nope. Simply because x and y change during every iteration not during every request. – BalusC Sep 18 '15 at 14:33
  • 1
    @BalusC well so the scope is irrelevant here, it ain't gonna work no matter what. – Ced Sep 18 '15 at 14:38
  • Ok, but you don't need to evaluate all these times. Inside a `c:forEach` loop, `x` and `y` will not change, so we would benefit by having a var inside the loop. Is there any scope that would force to re-evaluate only once per `c:forEach` ? – yannicuLar Sep 18 '15 at 15:57