org.apache.wookie.w3c.W3CWidgetFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wookie.w3c.W3CWidgetFactory.java

Source

/*
 *  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.apache.wookie.w3c;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;

import org.apache.commons.compress.archivers.zip.ZipFile;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.apache.wookie.w3c.exceptions.BadManifestException;
import org.apache.wookie.w3c.exceptions.BadWidgetZipFileException;
import org.apache.wookie.w3c.exceptions.InvalidContentTypeException;
import org.apache.wookie.w3c.exceptions.InvalidStartFileException;
import org.apache.wookie.w3c.impl.WidgetManifestModel;
import org.apache.wookie.w3c.util.WidgetPackageUtils;

/**
 * Factory for parsing W3C Widget packages (.wgt files).
 * 
 * <p>To use the factory you MUST supply a valid output directory into which the Factory will unpack the widget. Other factory
 * properties are optional.<p>
 * 
 * <p>Factory properties:</p>
 * 
 * <dl>
 *   <dt>outputDirectory</dt>
 *   <dd>The directory where the widget will be saved. The factory will expand the widget archive into this
 *   directory, using the widget's identifier to generate a directory name and structure in which to place it</dd>
 *   <dt>startPageProcessor</dt>
 *   <dd>An implementation of the IStartPageProcessor interface. Setting this property allows you to inject a class that can pre-process
 *   start pages for the widget; for example to inject javascript, tidy up HTML, insert template code etc. If this is not set, 
 *   no pre-processing is done by the factory.</dd>
 *   <dt>locales</dt>
 *   <dd>The supported locales (as BCP47 language-tags) to be processed for the widget. This determines which start files, icons, and other localized elements
 *   will be processed and expanded. This is set to "en" by default</dd>
 *   <dt>encodings</dt>
 *   <dd>The supported encodings to be processed for the widget. This determines which custom encodings will be allowed for start files. 
 *   This is set to UTF-8 by default.</dd>
 *   <dt>localPath</dt>
 *   <dd>The base URL from which unpacked widgets will be served, e.g. "/myapp/widgets". The URLs of start files will be appended to
 *   this base URL to create the widget URL. The default value of this property is "/widgets"</dd>
 *   <dt>features</dt>
 *   <dd>The features supported by the implementation; this should be supplied as IRIs e.g. "http://wave.google.com". The features
 *   are matched against features requested by the widget; if the widget requires features that are unsupported, an Exception will be
 *   thrown when parsing the widget package. The default value of this property is an empty String array.</dd>
 * </dl>
 * 
 */
public class W3CWidgetFactory {

    // Get the logger
    static Logger _logger = Logger.getLogger(W3CWidgetFactory.class.getName());
    // this value is set by the parser
    private File unzippedWidgetDirectory;
    private File outputDirectory;
    private IStartPageProcessor startPageProcessor;
    private String[] locales;
    private String localPath;
    private String[] features;
    private String[] encodings;

    /**
     * Set the features to be included when parsing widgets
     * @param features
     */
    public void setFeatures(String[] features) {
        this.features = features;
    }

    public W3CWidgetFactory() {
        // Defaults
        this.locales = new String[] { "en" };
        this.features = new String[0];
        this.localPath = "/widgets";
        this.outputDirectory = null;
        this.encodings = new String[] { "UTF-8" };
        this.startPageProcessor = new IStartPageProcessor() {
            public void processStartFile(File startFile, W3CWidget model) throws Exception {
            }

        };
    }

    /**
     * Set the directory to use to save widgets.
     * @param outputDirectory
     * @throws IOException if the directory does not exist
     */
    public void setOutputDirectory(final String outputDirectory) throws IOException {
        if (outputDirectory == null)
            throw new NullPointerException();
        File file = new File(outputDirectory);
        if (!file.exists())
            throw new FileNotFoundException("the output directory does not exist");
        if (!file.canWrite())
            throw new IOException("the output directory cannot be written to");
        if (!file.isDirectory())
            throw new IOException("the output directory is not a folder");
        this.outputDirectory = file;
    }

    /**
     * Set the start page processor to use when parsing widgets
     * @param startPageProcessor
     */
    public void setStartPageProcessor(final IStartPageProcessor startPageProcessor) {
        this.startPageProcessor = startPageProcessor;
    }

    /**
     * Set the supported locales to be used when parsing widgets
     * @param locales
     */
    public void setLocales(final String[] locales) {
        if (locales == null)
            throw new NullPointerException("locales cannot be specified as Null");
        this.locales = locales;
    }

    /**
     * Set the base URL to use
     * @param localPath
     * @throws Exception 
     */
    public void setLocalPath(final String localPath) {
        if (localPath == null)
            throw new NullPointerException("local path cannot be set to Null");
        this.localPath = localPath;
    };

    /**
     * Parse a given ZipFile and return a W3CWidget object representing the processed information in the package.
     * The widget will be saved in the outputFolder.
     * 
     * @param zipFile
     * @return the widget model
     * @throws BadWidgetZipFileException if there is a problem with the zip package
     * @throws BadManifestException if there is a problem with the config.xml manifest file in the package
     */
    public W3CWidget parse(final File zipFile) throws Exception, BadWidgetZipFileException, BadManifestException {
        if (outputDirectory == null)
            throw new Exception(
                    "No output directory has been set; use setOutputDirectory(File) to set the location to output widget files");
        return processWidgetPackage(zipFile);
    }

    /**
     * Parse a widget at a given URL and return a W3CWidget object representing the processed information in the package.
     * The widget will be saved in the outputFolder.
     * @param url
     * @return
     * @throws BadWidgetZipFileException if there is a problem with the zip package
     * @throws BadManifestException if there is a problem with the config.xml manifest file in the package
     * @throws InvalidContentTypeException if the widget has an invalid content type
     * @throws IOException if the widget cannot be downloaded
     */
    public W3CWidget parse(final URL url) throws BadWidgetZipFileException, BadManifestException,
            InvalidContentTypeException, IOException, Exception {
        File file = download(url, false);
        return parse(file);
    }

    /**
     * Parse a widget at a given URL and return a W3CWidget object representing the processed information in the package.
     * The widget will be saved in the outputFolder.
     * @param url
     * @param ignoreContentType set to true to instruct the parser to ignore invalid content type exceptions
     * @return
     * @throws BadWidgetZipFileException if there is a problem with the zip package
     * @throws BadManifestException if there is a problem with the config.xml manifest file in the package
     * @throws InvalidContentTypeException if the widget has an invalid content type
     * @throws IOException if the widget cannot be downloaded
     */
    public W3CWidget parse(final URL url, boolean ignoreContentType) throws BadWidgetZipFileException,
            BadManifestException, InvalidContentTypeException, IOException, Exception {
        File file = download(url, ignoreContentType);
        return parse(file);
    }

    /**
     * The standard MIME type for a W3C Widget
     */
    private static final String WIDGET_CONTENT_TYPE = "application/widget";

    /**
     * Download widget from given URL
     * @param url the URL to download
     * @param ignoreContentType if set to true, will ignore invalid content types (not application/widget)
     * @return the File downloaded
     * @throws InvalidContentTypeException 
     * @throws HttpException
     * @throws IOException
     */
    private File download(URL url, boolean ignoreContentType)
            throws InvalidContentTypeException, HttpException, IOException {
        HttpClient client = new HttpClient();
        GetMethod method = new GetMethod(url.toString());
        client.executeMethod(method);
        String type = method.getResponseHeader("Content-Type").getValue();
        if (!ignoreContentType && !type.startsWith(WIDGET_CONTENT_TYPE))
            throw new InvalidContentTypeException("Problem downloading widget: expected a content type of "
                    + WIDGET_CONTENT_TYPE + " but received:" + type);
        File file = File.createTempFile("wookie", null);
        FileUtils.writeByteArrayToFile(file, IOUtils.toByteArray(method.getResponseBodyAsStream()));
        method.releaseConnection();
        return file;
    }

    public void setEncodings(final String[] encodings) throws Exception {
        if (encodings == null)
            throw new NullPointerException("Supported encodings cannot be set to null");
        if (encodings.length == 0)
            throw new Exception("At least one encoding must be specified");
        this.encodings = encodings;
    }

    /**
     * Process a widget package for the given zip file
     * @param zipFile
     * @return a W3CWidget representing the widget
     * @throws BadWidgetZipFileException
     * @throws BadManifestException
     */
    private W3CWidget processWidgetPackage(File zipFile) throws BadWidgetZipFileException, BadManifestException {
        ZipFile zip;
        try {
            zip = new ZipFile(zipFile);
        } catch (IOException e) {
            throw new BadWidgetZipFileException();
        }
        if (WidgetPackageUtils.hasManifest(zip)) {
            try {
                // build the model
                WidgetManifestModel widgetModel = new WidgetManifestModel(WidgetPackageUtils.extractManifest(zip),
                        locales, features, encodings, zip);

                // get the widget identifier
                String manifestIdentifier = widgetModel.getIdentifier();
                // create the folder structure to unzip the zip into
                unzippedWidgetDirectory = WidgetPackageUtils.createUnpackedWidgetFolder(outputDirectory,
                        manifestIdentifier);
                // now unzip it into that folder
                WidgetPackageUtils.unpackZip(zip, unzippedWidgetDirectory);

                // Iterate over all start files and update paths
                for (IContentEntity content : widgetModel.getContentList()) {
                    // now update the js links in the start page
                    File startFile = new File(
                            unzippedWidgetDirectory.getCanonicalPath() + File.separator + content.getSrc());
                    String relativestartUrl = (WidgetPackageUtils.getURLForWidget(localPath, manifestIdentifier,
                            content.getSrc()));
                    content.setSrc(relativestartUrl);
                    if (startFile.exists() && startPageProcessor != null) {
                        startPageProcessor.processStartFile(startFile, widgetModel);
                    }
                }
                if (widgetModel.getContentList().isEmpty()) {
                    throw new InvalidStartFileException("Widget has no start page");
                }

                // get the path to the root of the unzipped folder
                String thelocalPath = WidgetPackageUtils.getURLForWidget(localPath, manifestIdentifier, "");
                // now pass this to the model which will prepend the path to local resources (not web icons)
                widgetModel.updateIconPaths(thelocalPath);

                // check to see if this widget already exists in the DB - using the ID (guid) key from the manifest
                return widgetModel;
            } catch (InvalidStartFileException e) {
                throw e;
            } catch (BadManifestException e) {
                throw e;
            } catch (Exception e) {
                throw new BadManifestException(e);
            }
        } else {
            // no manifest file found in zip archive
            throw new BadWidgetZipFileException(); //$NON-NLS-1$ 
        }
    }

    public File getUnzippedWidgetDirectory() {
        return unzippedWidgetDirectory;
    }

}