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

org.omnifaces.resourcehandler.CombinedResourceInfo Maven / Gradle / Ivy

/*
 * Copyright 2012 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.FacesLocal.getRequestDomainURL;
import static org.omnifaces.util.Utils.isEmpty;
import static org.omnifaces.util.Utils.serializeURLSafe;
import static org.omnifaces.util.Utils.unserializeURLSafe;

import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.faces.application.Resource;
import javax.faces.application.ResourceHandler;
import javax.faces.context.FacesContext;

import org.omnifaces.el.functions.Converters;
import org.omnifaces.util.Utils;

/**
 * 

* This class is a wrapper which collects all combined resources and stores it in the cache. A builder has been provided * to create an instance of combined resource info and put it in the cache if absent. * * @author Bauke Scholtz */ public final class CombinedResourceInfo { // Constants ------------------------------------------------------------------------------------------------------ private static final Logger logger = Logger.getLogger(CombinedResourceHandler.class.getName()); private static final Map CACHE = new ConcurrentHashMap(); private static final String LOG_RESOURCE_NOT_FOUND = "CombinedResourceHandler: The resource %s cannot be found" + " and therefore a 404 will be returned for the combined resource ID %s"; // Properties ----------------------------------------------------------------------------------------------------- private String id; private Set resourceIdentifiers; private Set resources; private int contentLength; private long lastModified; // Constructors --------------------------------------------------------------------------------------------------- /** * Creates an instance of combined resource info based on the given ID and ordered set of resource identifiers. * @param resourceIdentifiers Ordered set of resource identifiers, which are to be combined in a single resource. */ private CombinedResourceInfo(String id, Set resourceIdentifiers) { this.id = id; this.resourceIdentifiers = resourceIdentifiers; } /** * Use this builder to create an instance of combined resource info and put it in the cache if absent. * @author Bauke Scholtz */ public static final class Builder { // Constants -------------------------------------------------------------------------------------------------- private static final String ERROR_EMPTY_RESOURCES = "There are no resources been added. Use add() method to add them or use isEmpty() to check beforehand."; // Properties ------------------------------------------------------------------------------------------------- private Set resourceIdentifiers = new LinkedHashSet(); // Actions ---------------------------------------------------------------------------------------------------- /** * Add the resource represented by the given resource identifier resources of this combined resource info. The * insertion order is maintained and duplicates are filtered. * @param resourceIdentifier The resource identifier of the resource to be added. * @return This builder. */ public Builder add(ResourceIdentifier resourceIdentifier) { resourceIdentifiers.add(resourceIdentifier); return this; } /** * Returns true if there are no resources been added. Use this method before {@link #create()} if it's unknown * if there are any resources been added. * @return True if there are no resources been added, otherwise false. */ public boolean isEmpty() { return resourceIdentifiers.isEmpty(); } /** * Creates the CombinedResourceInfo instance in cache if absent and return its ID. * @return The ID of the CombinedResourceInfo instance. * @throws IllegalStateException If there are no resources been added. So, to prevent it beforehand, use * the {@link #isEmpty()} method to check if there are any resources been added. */ public String create() { if (resourceIdentifiers.isEmpty()) { throw new IllegalStateException(ERROR_EMPTY_RESOURCES); } return get(toUniqueId(resourceIdentifiers)).id; } } /** * Returns the combined resource info identified by the given ID from the cache. A new one will be created based on * the given ID if absent in cache. * @param id The ID of the combined resource info to be returned from the cache. * @return The combined resource info identified by the given ID from the cache. */ public static CombinedResourceInfo get(String id) { CombinedResourceInfo info = CACHE.get(id); if (info == null) { Set resourceIdentifiers = fromUniqueId(id); if (resourceIdentifiers != null) { info = new CombinedResourceInfo(id, Collections.unmodifiableSet(resourceIdentifiers)); CACHE.put(id, info); } } return info; } // Actions -------------------------------------------------------------------------------------------------------- /** * Lazily load the combined resources so that the set of resources, the total content length and the last modified * are been initialized. If one of the resources cannot be resolved, then this will log a WARNING and leave the * resources empty. */ private synchronized void loadResources() { if (!isEmpty(resources)) { return; } FacesContext context = FacesContext.getCurrentInstance(); ResourceHandler handler = context.getApplication().getResourceHandler(); resources = new LinkedHashSet(); contentLength = 0; lastModified = 0; for (ResourceIdentifier resourceIdentifier : resourceIdentifiers) { Resource resource = handler.createResource(resourceIdentifier.getName(), resourceIdentifier.getLibrary()); if (resource == null) { logger.log(Level.WARNING, String.format(LOG_RESOURCE_NOT_FOUND, resourceIdentifier, id)); resources.clear(); return; } resources.add(resource); URLConnection connection; try { connection = resource.getURL().openConnection(); } catch (Exception richFacesDoesNotSupportThis) { try { connection = new URL(getRequestDomainURL(context) + resource.getRequestPath()).openConnection(); } catch (IOException ignore) { // Can't and shouldn't handle it at this point. // It would be thrown during resource streaming anyway which is a better moment. return; } } contentLength += connection.getContentLength(); long resourceLastModified = connection.getLastModified(); if (resourceLastModified > lastModified) { lastModified = resourceLastModified; } } } /** * Returns true if the given object is also an instance of {@link CombinedResourceInfo} and its ID equals to the * ID of the current combined resource info instance. */ @Override public boolean equals(Object other) { return (other instanceof CombinedResourceInfo) ? ((CombinedResourceInfo) other).id.equals(id) : false; } /** * Returns the sum of the hash code of this class and the ID. */ @Override public int hashCode() { return getClass().hashCode() + id.hashCode(); } /** * Returns the string representation of this combined resource info in the format of *

CombinedResourceInfo[id,resourceIdentifiers]
* Where id is the unique ID and resourceIdentifiers is the ordered set of all resource * identifiers as is been created with the builder. */ @Override public String toString() { return String.format("CombinedResourceInfo[%s,%s]", id, resourceIdentifiers); } // Getters -------------------------------------------------------------------------------------------------------- /** * Returns the ordered set of resource identifiers of this combined resource info. * @return the ordered set of resource identifiers of this combined resource info. */ public Set getResourceIdentifiers() { return resourceIdentifiers; } /** * Returns the ordered set of resources of this combined resource info. * @return The ordered set of resources of this combined resource info. */ public Set getResources() { loadResources(); return resources; } /** * Returns the content length in bytes of this combined resource info. * @return The content length in bytes of this combined resource info. */ public int getContentLength() { loadResources(); return contentLength; } /** * Returns the last modified timestamp in milliseconds of this combined resource info. * @return The last modified timestamp in milliseconds of this combined resource info. */ public long getLastModified() { loadResources(); return lastModified; } // Helpers ---------------------------------------------------------------------------------------------------- /** * Create an unique ID based on the given set of resource identifiers. The current implementation converts the * set to a |-delimited string which is serialized using {@link Utils#serialize(String)}. * @param resourceIdentifiers The set of resource identifiers to create an unique ID for. * @return The unique ID of the given set of resource identifiers. */ private static String toUniqueId(Set resourceIdentifiers) { return serializeURLSafe(Converters.joinCollection(resourceIdentifiers, "|")); } /** * Create an ordered set of resource identifiers based on the given unique ID. This does the reverse of * {@link #toUniqueId(Map)}. * @param id The unique ID of the set of resource identifiers. * @return The set of resource identifiers based on the given unique ID, or null if the ID is not * valid. */ private static Set fromUniqueId(String id) { String resourcesId; try { resourcesId = unserializeURLSafe(id); } catch (IllegalArgumentException e) { // This will occur when the ID has purposefully been manipulated for some reason. // Just return null then so that it will end up in a 404. return null; } Set resourceIdentifiers = new LinkedHashSet(); for (String resourceIdentifier : resourcesId.split("\\|")) { resourceIdentifiers.add(new ResourceIdentifier(resourceIdentifier)); } return resourceIdentifiers; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy