All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.myfaces.view.facelets.impl.CacheELFaceletCacheImpl Maven / Gradle / Ivy

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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 org.apache.myfaces.view.facelets.impl;

import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import jakarta.faces.view.facelets.FaceletContext;
import jakarta.faces.view.facelets.FaceletException;

import org.apache.myfaces.resource.ResourceLoaderUtils;
import org.apache.myfaces.core.api.shared.lang.Assert;
import org.apache.myfaces.view.facelets.AbstractFaceletCache;
import org.apache.myfaces.view.facelets.AbstractFaceletContext;

/**
 * Extended MyFaces specific FaceletCache implementation that recompile
 * facelet instance when a template context param is found.
 * 
 * @author Leonardo Uribe
 * @since 2.1.0
 *
 */
class CacheELFaceletCacheImpl extends AbstractFaceletCache
{

    private static final long INFINITE_DELAY = -1;
    private static final long NO_CACHE_DELAY = 0;
    
    /**
     * FaceletNode is necessary only here, because view metadata and 
     * composite component metadata are special and does not allow use nested
     * template tags. View metadata facelet trims everything outside f:metadata
     * and composite component metadata only takes into account composite:xxx tags,
     * ignoring ui:xxx tags.
     */
    private Map _facelets;
    
    private Map _viewMetadataFacelets;
    
    private Map _compositeComponentMetadataFacelets;
    
    private long _refreshPeriod;
    
    CacheELFaceletCacheImpl(long refreshPeriod)
    {
        _refreshPeriod = refreshPeriod < 0 ? INFINITE_DELAY : refreshPeriod * 1000;

        _facelets = new ConcurrentHashMap<>();
        _viewMetadataFacelets = new ConcurrentHashMap<>();
        _compositeComponentMetadataFacelets = new ConcurrentHashMap<>();
    }

    @Override
    public DefaultFacelet getFacelet(URL url) throws IOException
    {
        Assert.notNull(url, "url");
        
        String key = url.toString();
        
        FaceletNode node = _facelets.get(key);
        DefaultFacelet f = node != null ? node.getFacelet() : null;
        
        if (f == null || this.needsToBeRefreshed(f))
        {
            Set paramsSet = null;
            if (node != null)
            {
                paramsSet = node.getParams();
            }
            f = getMemberFactory().newInstance(url);
            if (_refreshPeriod != NO_CACHE_DELAY)
            {
                _facelets.put(key, (paramsSet != null && !paramsSet.isEmpty()) ? 
                        new FaceletNode(f, paramsSet) : new FaceletNode(f) );
            }
        }
        
        return f;
    }

    @Override
    public DefaultFacelet getFacelet(FaceletContext ctx, URL url) throws IOException
    {
        String key = url.toString();
        
        //1. Check that the current parameters on the template are known
        //   for the template.
        //2. If all current parameters are known return the template
        //2. If some current parameter is not known, add the param(s) to the
        //   template, register the known params in the template context and
        //   recompile the facelet, to clean up al EL expressions at once.

        FaceletNode node = _facelets.get(key);
        DefaultFacelet f = (node != null) ? node.getFacelet() : null;
        
        Set paramsSet = Collections.emptySet();
        paramsSet = (node != null) ? node.getParams() : paramsSet;

        AbstractFaceletContext actx = (AbstractFaceletContext) ctx;
        Set knownParameters = actx.getTemplateContext().isKnownParametersEmpty() ?
            (Set) Collections.emptySet() : actx.getTemplateContext().getKnownParameters();
        
        boolean create = false;
        for (String paramKey : knownParameters)
        {
            if (!paramsSet.contains(paramKey))
            {
                create = true;
                break;
            }
        }
        
        if (f == null || this.needsToBeRefreshed(f) || create)
        {
            f = getMemberFactory().newInstance(url);
            if (_refreshPeriod != NO_CACHE_DELAY)
            {
                if (!paramsSet.isEmpty()|| !knownParameters.isEmpty() )
                {
                    paramsSet = new HashSet(paramsSet);
                    paramsSet.addAll(knownParameters);
                    _facelets.put(key, new FaceletNode(f, paramsSet));
                }
                else
                {
                    _facelets.put(key, new FaceletNode(f));
                }
            }
        }

        if (!paramsSet.isEmpty())
        {
            for (String param : paramsSet)
            {
                if (!actx.getTemplateContext().containsKnownParameter(param))
                {
                    actx.getTemplateContext().addKnownParameters(param);
                }
            }
        }
        
        return f;
    }
    
    @Override
    public boolean isFaceletCached(URL url)
    {
        return _facelets.containsKey(url.toString());
    }

    @Override
    public DefaultFacelet getViewMetadataFacelet(URL url) throws IOException
    {
        Assert.notNull(url, "url");
        
        String key = url.toString();
        
        DefaultFacelet f = _viewMetadataFacelets.get(key);
        
        if (f == null || this.needsToBeRefreshed(f))
        {
            f = getMetadataMemberFactory().newInstance(url);
            if (_refreshPeriod != NO_CACHE_DELAY)
            {
                _viewMetadataFacelets.put(key, f);
            }
        }
        
        return f;
    }

    @Override
    public boolean isViewMetadataFaceletCached(URL url)
    {
        return _viewMetadataFacelets.containsKey(url.toString());
    }

    /**
     * Template method for determining if the Facelet needs to be refreshed.
     * 
     * @param facelet
     *            Facelet that could have expired
     * @return true if it needs to be refreshed
     */
    protected boolean needsToBeRefreshed(DefaultFacelet facelet)
    {
        // if set to 0, constantly reload-- nocache
        if (_refreshPeriod == NO_CACHE_DELAY)
        {
            return true;
        }

        // if set to -1, never reload
        if (_refreshPeriod == INFINITE_DELAY)
        {
            return false;
        }

        long target = facelet.getCreateTime() + _refreshPeriod;
        if (System.currentTimeMillis() > target)
        {
            // Should check for file modification
            try
            {
                long lastModified = ResourceLoaderUtils.getResourceLastModified(facelet.getSource());

                return lastModified == 0 || lastModified > target;
            }
            catch (IOException e)
            {
                throw new FaceletException("Error Checking Last Modified for " + facelet.getAlias(), e);
            }
        }

        return false;
    }

    @Override
    public DefaultFacelet getCompositeComponentMetadataFacelet(URL url) throws IOException
    {
        Assert.notNull(url, "url");

        String key = url.toString();

        DefaultFacelet f = _compositeComponentMetadataFacelets.get(key);

        if (f == null || this.needsToBeRefreshed(f))
        {
            f = getCompositeComponentMetadataMemberFactory().newInstance(url);
            if (_refreshPeriod != NO_CACHE_DELAY)
            {
                _compositeComponentMetadataFacelets.put(key, f);
            }
        }
        return f;
    }

    @Override
    public boolean isCompositeComponentMetadataFaceletCached(URL url)
    {
        return _compositeComponentMetadataFacelets.containsKey(url.toString());
    }
    
    private static class FaceletNode
    {
        private DefaultFacelet facelet;
        private Set params;

        public FaceletNode(DefaultFacelet facelet)
        {
            this.facelet = facelet;
            this.params = Collections.emptySet();
        }
        
        public FaceletNode(DefaultFacelet facelet, Set params)
        {
            this.facelet = facelet;
            this.params = params;
        }

        public DefaultFacelet getFacelet()
        {
            return facelet;
        }

        public void setFacelet(DefaultFacelet facelet)
        {
            this.facelet = facelet;
        }

        public Set getParams()
        {
            return params;
        }

        public void setParams(Set params)
        {
            this.params = params;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy