/* * Copyright 2017 OmniFaces * * Licensed 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.omnifaces.resourcehandler; import static org.omnifaces.util.Faces.getMimeType; import static org.omnifaces.util.Utils.toByteArray; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import javax.faces.application.Resource; import javax.faces.context.FacesContext; import org.omnifaces.component.output.cache.Cache; import org.omnifaces.component.output.cache.CacheFactory; /** * <p> * This {@link Resource} implementation holds all the necessary information about combined resources in order to * properly serve combined resources on a single HTTP request. * * @author Bauke Scholtz * @author Stephan Rauh {@literal <www.beyondjava.net>} */ public class CombinedResource extends DynamicResource { // Constants ------------------------------------------------------------------------------------------------------ private static final String CACHE_SCOPE = "application"; // Properties ----------------------------------------------------------------------------------------------------- private String resourceId; private CombinedResourceInfo info; private Integer cacheTTL; // Constructors --------------------------------------------------------------------------------------------------- /** * Constructs a new combined resource based on the given resource name. This constructor is only used by * {@link CombinedResourceHandler#createResource(String, String)}. * @param resourceName The resource name of the combined resource. * @param cacheTTL The combined resource content cache TTL. */ public CombinedResource(String resourceName, Integer cacheTTL) { super(resourceName, CombinedResourceHandler.LIBRARY_NAME, getMimeType(resourceName)); String[] resourcePathParts = resourceName.split("\\.", 2)[0].split("/"); resourceId = resourcePathParts[resourcePathParts.length - 1]; info = CombinedResourceInfo.get(resourceId); this.cacheTTL = cacheTTL; } // Actions -------------------------------------------------------------------------------------------------------- @Override public long getLastModified() { return (info != null) ? info.getLastModified() : super.getLastModified(); } @Override public InputStream getInputStream() throws IOException { if (info != null && !info.getResources().isEmpty()) { if (cacheTTL == null) { return new CombinedResourceInputStream(info.getResources()); } else { return getInputStreamFromCache(); } } else { return null; } } /** * Returns the cached input stream, or if there is none, then create one. */ private InputStream getInputStreamFromCache() throws IOException { Cache combinedResourceCache = CacheFactory.getCache(FacesContext.getCurrentInstance(), CACHE_SCOPE); byte[] cachedCombinedResource; synchronized (CombinedResourceHandler.class) { cachedCombinedResource = (byte[]) combinedResourceCache.getObject(resourceId); } if (cachedCombinedResource == null) { cachedCombinedResource = toByteArray(new CombinedResourceInputStream(info.getResources())); synchronized (CombinedResourceHandler.class) { if (combinedResourceCache.getObject(resourceId) == null) { combinedResourceCache.putObject(resourceId, cachedCombinedResource, cacheTTL); } } } return new ByteArrayInputStream(cachedCombinedResource); } }