org.ajax4jsf.resource.ResourceBuilderImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.ajax4jsf.resource.ResourceBuilderImpl.java

Source

/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package org.ajax4jsf.resource;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StreamCorruptedException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.Deflater;
import java.util.zip.Inflater;

import javax.faces.FacesException;
import javax.faces.context.FacesContext;
import javax.imageio.ImageIO;
import javax.servlet.ServletContext;

import org.ajax4jsf.Messages;
import org.ajax4jsf.resource.util.URLToStreamHelper;
import org.ajax4jsf.util.base64.Codec;
import org.ajax4jsf.webapp.WebXml;
import org.apache.commons.digester.Digester;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * Produce instances of InternetResource's for any types - jar resource, dynamic
 * created image, component-incapsulated etc. Realised as singleton class to
 * support cache, configuration etc.
 * 
 * @author shura (latest modification by $Author: alexsmirnov $)
 * @version $Revision: 1.1.2.1 $ $Date: 2007/01/09 18:56:58 $
 * 
 */
public class ResourceBuilderImpl extends InternetResourceBuilder {

    private static final Log log = LogFactory.getLog(ResourceBuilderImpl.class);

    private static final String DATA_SEPARATOR = "/DATA/";
    private static final String DATA_BYTES_SEPARATOR = "/DATB/";

    private static final Pattern DATA_SEPARATOR_PATTERN = Pattern.compile("/DAT(A|B)/");

    private static final ResourceRenderer defaultRenderer = new MimeRenderer(null);

    private Map<String, ResourceRenderer> renderers;
    /**
     * keep resources instances . TODO - put this map to application-scope
     * attribute, for support clastering environment.
     */
    private Map<String, InternetResource> resources = Collections
            .synchronizedMap(new HashMap<String, InternetResource>());

    private long _startTime;

    private Codec codec;

    private static final ResourceRenderer scriptRenderer = new ScriptRenderer();

    private static final ResourceRenderer styleRenderer = new StyleRenderer();

    static {
        // renderers.put(".htc",new BehaviorRenderer());
        // set in-memory caching ImageIO
        Thread thread = Thread.currentThread();
        ClassLoader initialTCCL = thread.getContextClassLoader();

        try {
            ClassLoader systemCL = ClassLoader.getSystemClassLoader();
            thread.setContextClassLoader(systemCL);
            ImageIO.setUseCache(false);
        } finally {
            thread.setContextClassLoader(initialTCCL);
        }

    }

    public WebXml getWebXml(FacesContext context) {
        WebXml webXml = WebXml.getInstance(context);
        if (null == webXml) {
            throw new FacesException(
                    "Resources framework is not initialised, check web.xml for Filter configuration");
        }
        return webXml;
    }

    public ResourceBuilderImpl() {
        super();
        _startTime = System.currentTimeMillis();
        codec = new Codec();
        renderers = new HashMap<String, ResourceRenderer>();
        // append known renderers for extentions.
        renderers.put(".gif", new GifRenderer());
        ResourceRenderer renderer = new JpegRenderer();
        renderers.put(".jpeg", renderer);
        renderers.put(".jpg", renderer);
        renderers.put(".png", new PngRenderer());
        renderers.put(".js", getScriptRenderer());
        renderers.put(".css", getStyleRenderer());
        renderers.put(".log", new LogfileRenderer());
        renderers.put(".html", new HTMLRenderer());
        renderers.put(".xhtml", new MimeRenderer("application/xhtml+xml"));
        renderers.put(".xml", new MimeRenderer("text/xml"));
        renderers.put(".xcss", new TemplateCSSRenderer());
        renderers.put(".swf", new MimeRenderer("application/x-shockwave-flash"));
    }

    /**
     * @throws FacesException
     */
    protected void registerResources() throws FacesException {
        try {
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            Enumeration<URL> e = loader.getResources("META-INF/resources-config.xml");
            while (e.hasMoreElements()) {
                URL resource = e.nextElement();
                registerConfig(resource);
            }
        } catch (IOException e) {
            throw new FacesException(e);
        }
    }

    private void registerConfig(URL resourceConfig) {
        try {
            if (log.isDebugEnabled()) {
                log.debug("Process resources configuration file " + resourceConfig.toExternalForm());
            }

            InputStream in = URLToStreamHelper.urlToStream(resourceConfig);
            try {
                Digester digester = new Digester();
                digester.setValidating(false);
                digester.setEntityResolver(new EntityResolver() {
                    // Dummi resolver - alvays do nothing
                    public InputSource resolveEntity(String publicId, String systemId)
                            throws SAXException, IOException {
                        return new InputSource(new StringReader(""));
                    }
                });
                digester.setNamespaceAware(false);
                digester.setUseContextClassLoader(true);
                digester.push(this);
                digester.addObjectCreate("resource-config/resource", "class", JarResource.class);
                digester.addObjectCreate("resource-config/resource/renderer", "class", HTMLRenderer.class);
                digester.addCallMethod("resource-config/resource/renderer/content-type", "setContentType", 0);
                digester.addSetNext("resource-config/resource/renderer", "setRenderer",
                        ResourceRenderer.class.getName());
                digester.addCallMethod("resource-config/resource/name", "setKey", 0);
                digester.addCallMethod("resource-config/resource/path", "setPath", 0);
                digester.addCallMethod("resource-config/resource/cacheable", "setCacheable", 0);
                digester.addCallMethod("resource-config/resource/session-aware", "setSessionAware", 0);
                digester.addCallMethod("resource-config/resource/property", "setProperty", 2);
                digester.addCallParam("resource-config/resource/property/name", 0);
                digester.addCallParam("resource-config/resource/property/value", 1);
                digester.addCallMethod("resource-config/resource/content-type", "setContentType", 0);
                digester.addSetNext("resource-config/resource", "addResource", InternetResource.class.getName());
                digester.parse(in);
            } finally {
                in.close();
            }
        } catch (IOException e) {
            throw new FacesException(e);
        } catch (SAXException e) {
            throw new FacesException(e);
        }
    }

    /**
     */
    public void init() throws FacesException {
        // TODO - mace codec configurable.
        registerResources();
    }

    /**
     * Base point for creating resource. Must detect type and build appropriate
     * instance. Currently - make static resource for ordinary request, or
     * instance of class.
     * 
     * @param base
     *            base object for resource ( resourcess in classpath will be get
     *            relative to it package )
     * @param path
     *            key - path to resource, resource class name etc.
     * @return
     * @throws FacesException
     */
    public InternetResource createResource(Object base, String path) throws FacesException {
        // TODO - detect type of resource ( for example, resources location path
        // in Skin
        try {
            return getResource(path);
        } catch (ResourceNotFoundException e) {
            try {
                return getResource(buildKey(base, path));
            } catch (ResourceNotFoundException e1) {
                if (log.isDebugEnabled()) {
                    log.debug(Messages.getMessage(Messages.BUILD_RESOURCE_INFO, path));
                }
            }
        }
        // path - is class name ?
        InternetResource res;
        try {
            Class<?> resourceClass = Class.forName(path);
            res = createDynamicResource(path, resourceClass);
        } catch (Exception e) {
            try {
                res = createJarResource(base, path);
            } catch (ResourceNotFoundException ex) {
                res = createStaticResource(path);
            }
            // TODO - if resource not found, create static ?
        }
        return res;
    }

    private String buildKey(Object base, String path) {
        if (path.startsWith("/")) {
            return path.substring(1);
        }
        if (null == base) {
            return path;
        }
        StringBuffer packageName = new StringBuffer(base.getClass().getPackage().getName().replace('.', '/'));
        return packageName.append("/").append(path).toString();
    }

    public String getUri(InternetResource resource, FacesContext context, Object storeData) {
        StringBuffer uri = new StringBuffer();// ResourceServlet.DEFAULT_SERVLET_PATH).append("/");
        uri.append(resource.getKey());
        // append serialized data as Base-64 encoded request string.
        if (storeData != null) {
            try {
                byte[] objectData;
                if (storeData instanceof byte[]) {
                    objectData = (byte[]) storeData;
                    uri.append(DATA_BYTES_SEPARATOR);
                } else {
                    ByteArrayOutputStream dataSteram = new ByteArrayOutputStream(1024);
                    ObjectOutputStream objStream = new ObjectOutputStream(dataSteram);
                    objStream.writeObject(storeData);
                    objStream.flush();
                    objStream.close();
                    dataSteram.close();
                    objectData = dataSteram.toByteArray();
                    uri.append(DATA_SEPARATOR);
                }
                byte[] dataArray = encrypt(objectData);
                uri.append(new String(dataArray, "ISO-8859-1"));

                // / byte[] objectData = dataSteram.toByteArray();
                // / uri.append("?").append(new
                // String(Base64.encodeBase64(objectData),
                // / "ISO-8859-1"));
            } catch (Exception e) {
                // Ignore errors, log it
                log.error(Messages.getMessage(Messages.QUERY_STRING_BUILDING_ERROR), e);
            }
        }

        boolean isGlobal = !resource.isSessionAware();

        String resourceURL = getWebXml(context).getFacesResourceURL(context, uri.toString(), isGlobal);// context.getApplication().getViewHandler().getResourceURL(context,uri.toString());
        if (!isGlobal) {
            resourceURL = context.getExternalContext().encodeResourceURL(resourceURL);
        }
        if (log.isDebugEnabled()) {
            log.debug(Messages.getMessage(Messages.BUILD_RESOURCE_URI_INFO, resource.getKey(), resourceURL));
        }
        return resourceURL;// context.getExternalContext().encodeResourceURL(resourceURL);

    }

    /**
     * @param key
     * @return
     */
    public InternetResource getResourceForKey(String key) throws ResourceNotFoundException {

        Matcher matcher = DATA_SEPARATOR_PATTERN.matcher(key);
        if (matcher.find()) {
            int data = matcher.start();
            key = key.substring(0, data);
        }

        return getResource(key);
    }

    public Object getResourceDataForKey(String key) {
        Object data = null;
        String dataString = null;
        Matcher matcher = DATA_SEPARATOR_PATTERN.matcher(key);
        if (matcher.find()) {
            if (log.isDebugEnabled()) {
                log.debug(Messages.getMessage(Messages.RESTORE_DATA_FROM_RESOURCE_URI_INFO, key, dataString));
            }
            int dataStart = matcher.end();
            dataString = key.substring(dataStart);
            byte[] objectArray = null;
            byte[] dataArray;
            try {
                dataArray = dataString.getBytes("ISO-8859-1");
                objectArray = decrypt(dataArray);
            } catch (UnsupportedEncodingException e1) {
                // default encoding always presented.
            }
            if ("B".equals(matcher.group(1))) {
                data = objectArray;
            } else {
                try {
                    ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(objectArray));
                    data = in.readObject();
                } catch (StreamCorruptedException e) {
                    log.error(Messages.getMessage(Messages.STREAM_CORRUPTED_ERROR), e);
                } catch (IOException e) {
                    log.error(Messages.getMessage(Messages.DESERIALIZE_DATA_INPUT_ERROR), e);
                } catch (ClassNotFoundException e) {
                    log.error(Messages.getMessage(Messages.DATA_CLASS_NOT_FOUND_ERROR), e);
                }
            }
        }

        return data;
    }

    public InternetResource getResource(String path) throws ResourceNotFoundException {

        InternetResource internetResource = (InternetResource) resources.get(path);
        if (null == internetResource) {
            throw new ResourceNotFoundException("Resource not registered : " + path);
        } else {
            return internetResource;
        }
    }

    public void addResource(InternetResource resource) {
        resources.put(resource.getKey(), resource);
        ResourceRenderer renderer = resource.getRenderer(null);
        if (renderer == null) {
            setRenderer(resource, resource.getKey());
        }
    }

    public void addResource(String key, InternetResource resource) {
        resources.put(key, resource);
        resource.setKey(key);
        // TODO - set renderer ?
    }

    // public String getFacesResourceKey(HttpServletRequest request) {
    // return getWebXml(context).getFacesResourceKey(request);
    // }

    public String getFacesResourceURL(FacesContext context, String Url, boolean isGlobal) {
        return getWebXml(context).getFacesResourceURL(context, Url, isGlobal);
    }

    /**
     * Build resource for link to static context in webapp.
     * 
     * @param path
     * @return
     * @throws FacesException
     */
    protected InternetResource createStaticResource(String path) throws ResourceNotFoundException, FacesException {
        FacesContext context = FacesContext.getCurrentInstance();
        if (null != context) {
            if (context.getExternalContext().getContext() instanceof ServletContext) {
                ServletContext servletContext = (ServletContext) context.getExternalContext().getContext();
                InputStream in = servletContext.getResourceAsStream(path);
                if (null != in) {
                    InternetResourceBase res = new StaticResource(path);
                    setRenderer(res, path);
                    res.setLastModified(new Date(getStartTime()));
                    addResource(path, res);
                    try {
                        in.close();
                    } catch (IOException e) {
                    }
                    return res;
                }
            }
        }
        throw new ResourceNotFoundException(Messages.getMessage(Messages.STATIC_RESOURCE_NOT_FOUND_ERROR, path));
    }

    private void setRenderer(InternetResource res, String path) throws FacesException {
        int lastPoint = path.lastIndexOf('.');
        if (lastPoint > 0) {
            String ext = path.substring(lastPoint);
            ResourceRenderer resourceRenderer = getRendererByExtension(ext);
            if (null != resourceRenderer) {
                res.setRenderer(resourceRenderer);
            } else {
                if (log.isDebugEnabled()) {
                    log.debug(
                            Messages.getMessage(Messages.NO_RESOURCE_REGISTERED_ERROR_2, path, renderers.keySet()));
                }

                // String mimeType = servletContext.getMimeType(path);
                res.setRenderer(defaultRenderer);
            }
        }
    }

    /**
     * @param ext
     * @return
     */
    protected ResourceRenderer getRendererByExtension(String ext) {
        return renderers.get(ext);
    }

    /**
     * Create resurce to send from classpath relative to base class.
     * 
     * @param base
     * @param path
     * @return
     * @throws FacesException
     */
    protected InternetResource createJarResource(Object base, String path)
            throws ResourceNotFoundException, FacesException {
        String key = buildKey(base, path);
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (null != loader.getResource(key)) {
            JarResource res = new JarResource(key);
            setRenderer(res, path);
            res.setLastModified(new Date(getStartTime()));
            addResource(key, res);
            return res;
        } else {
            throw new ResourceNotFoundException(Messages.getMessage(Messages.NO_RESOURCE_EXISTS_ERROR, key));
        }

    }

    /**
     * Create resource by instatiate given class.
     * 
     * @param path
     * @param instatiate
     * @return
     */
    protected InternetResource createDynamicResource(String path, Class<?> instatiate)
            throws ResourceNotFoundException {
        if (InternetResource.class.isAssignableFrom(instatiate)) {
            InternetResource resource;
            try {
                resource = (InternetResource) instatiate.newInstance();
                addResource(path, resource);
            } catch (Exception e) {
                String message = Messages.getMessage(Messages.INSTANTIATE_RESOURCE_ERROR, instatiate.toString());
                log.error(message, e);
                throw new ResourceNotFoundException(message, e);
            }
            return resource;
        }
        throw new FacesException(Messages.getMessage(Messages.INSTANTIATE_CLASS_ERROR));
    }

    /**
     * Create resource by instatiate {@link UserResource} class with given
     * properties ( or got from cache ).
     * 
     * @param cacheable
     * @param session
     * @param mime
     * @return
     * @throws FacesException
     */
    public InternetResource createUserResource(boolean cacheable, boolean session, String mime)
            throws FacesException {
        String path = getUserResourceKey(cacheable, session, mime);
        InternetResource userResource;
        try {
            userResource = getResource(path);
        } catch (ResourceNotFoundException e) {
            userResource = new UserResource(cacheable, session, mime);
            addResource(path, userResource);
        }
        return userResource;
    }

    /**
     * Generate resource key for user-generated resource with given properties.
     * 
     * @param cacheable
     * @param session
     * @param mime
     * @return
     */
    private String getUserResourceKey(boolean cacheable, boolean session, String mime) {
        StringBuffer pathBuffer = new StringBuffer(UserResource.class.getName());
        pathBuffer.append(cacheable ? "/c" : "/n");
        pathBuffer.append(session ? "/s" : "/n");
        if (null != mime) {
            pathBuffer.append('/').append(mime.hashCode());
        }
        String path = pathBuffer.toString();
        return path;
    }

    /**
     * @return Returns the startTime for application.
     */
    public long getStartTime() {
        return _startTime;
    }

    protected byte[] encrypt(byte[] src) {
        try {
            Deflater compressor = new Deflater(Deflater.BEST_SPEED);
            byte[] compressed = new byte[src.length + 100];
            compressor.setInput(src);
            compressor.finish();
            int totalOut = compressor.deflate(compressed);
            byte[] zipsrc = new byte[totalOut];
            System.arraycopy(compressed, 0, zipsrc, 0, totalOut);
            compressor.end();
            return codec.encode(zipsrc);
        } catch (Exception e) {
            throw new FacesException("Error encode resource data", e);
        }
    }

    protected byte[] decrypt(byte[] src) {
        try {
            byte[] zipsrc = codec.decode(src);
            Inflater decompressor = new Inflater();
            byte[] uncompressed = new byte[zipsrc.length * 5];
            decompressor.setInput(zipsrc);
            int totalOut = decompressor.inflate(uncompressed);
            byte[] out = new byte[totalOut];
            System.arraycopy(uncompressed, 0, out, 0, totalOut);
            decompressor.end();
            return out;
        } catch (Exception e) {
            throw new FacesException("Error decode resource data", e);
        }
    }

    @Override
    public ResourceRenderer getScriptRenderer() {
        return scriptRenderer;
    }

    @Override
    public ResourceRenderer getStyleRenderer() {
        return styleRenderer;
    }
}