Java tutorial
/* * 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; } }