Package org.apache.felix.ipojo.online.manipulator

Source Code of org.apache.felix.ipojo.online.manipulator.IPOJOURLHandler

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.felix.ipojo.online.manipulator;

import static java.lang.String.format;
import static org.apache.felix.ipojo.online.manipulator.Files.dump;
import static org.osgi.service.log.LogService.LOG_DEBUG;
import static org.osgi.service.log.LogService.LOG_WARNING;
import static org.osgi.service.url.URLConstants.URL_HANDLER_PROTOCOL;

import org.apache.felix.ipojo.annotations.Bind;
import org.apache.felix.ipojo.annotations.Component;
import org.apache.felix.ipojo.annotations.Instantiate;
import org.apache.felix.ipojo.annotations.Invalidate;
import org.apache.felix.ipojo.annotations.Provides;
import org.apache.felix.ipojo.annotations.Requires;
import org.apache.felix.ipojo.annotations.StaticServiceProperty;
import org.apache.felix.ipojo.annotations.Unbind;
import org.apache.felix.ipojo.manipulator.ManipulationVisitor;
import org.apache.felix.ipojo.manipulator.Reporter;
import org.apache.felix.ipojo.manipulator.ResourceStore;
import org.apache.felix.ipojo.manipulator.metadata.CompositeMetadataProvider;
import org.apache.felix.ipojo.manipulator.metadata.FileMetadataProvider;
import org.apache.felix.ipojo.manipulator.reporter.SystemReporter;
import org.apache.felix.ipojo.manipulator.spi.ModuleProvider;
import org.apache.felix.ipojo.manipulator.Pojoization;
import org.apache.felix.ipojo.manipulator.spi.Module;
import org.apache.felix.ipojo.manipulator.spi.provider.CompositeModuleProvider;
import org.apache.felix.ipojo.manipulator.spi.provider.CoreModuleProvider;
import org.apache.felix.ipojo.manipulator.spi.provider.DefaultModuleProvider;
import org.apache.felix.ipojo.manipulator.visitor.check.CheckFieldConsistencyVisitor;
import org.apache.felix.ipojo.manipulator.visitor.writer.ManipulatedResourcesWriter;
import org.osgi.framework.BundleContext;
import org.osgi.service.log.LogService;
import org.osgi.service.url.AbstractURLStreamHandlerService;
import org.osgi.service.url.URLStreamHandlerService;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
* iPOJO URL Handler allowing installation time manipulation.
*
* When a bundle is installed with the {@literal ipojo} URL prefix, the referred bundle is
* manipulated by this handler.

* The {@literal metadata.xml} file can either be provided:
* <ul>
*     <li>Inside the bundle, this handler will look for {@literal /metadata.xml} (at the root of the bundle)
*     or in {@literal /META-INF/metadata.xml}</li>
*     <li>Through the URL using the following URL format: {@literal ipojo:URL_BUNDLE!URL_METADATA}, notice
*     the {@literal !} used as separator</li>
* </ul>
*
* Examples of valid iPojo's URLs:
* <pre>
*     ipojo:file:///tmp/bundle.jar
*     ipojo:/http://www.example.org/bundle.jar
*     ipojo://mvn://com.acme/bundle/1.0.0  (with Maven Pax Url support installed)
*     ipojo:file:///tmp/bundle.jar!file:///tmp2/metadata.xml
* </pre>
*
* Notice that trailing {@literal '/'} (or {@literal '//'}) after {@literal ipojo:} are optional.
*
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
@Component(publicFactory = false,
           immediate = true)
@Provides(specifications = URLStreamHandlerService.class,
          properties = @StaticServiceProperty(name = URL_HANDLER_PROTOCOL,
                                              value = "ipojo",
                                              type = "java.lang.String"))
@Instantiate
public class IPOJOURLHandler extends AbstractURLStreamHandlerService {

    public static final String IPOJO_SCHEME = "ipojo:";

    private final BundleContext m_context;

    /**
     * The directory storing manipulated bundles.
     */
    private final File m_temp;

    private List<Module> m_modules = new ArrayList<Module>();

    //@Requires(optional = true, defaultimplementation = SystemLogService.class)
    // TODO Change this once NPE is fixed
    private LogService logger = new SystemLogService();

    @Bind(optional = true, aggregate = true)
    public void bindModule(Module module) {
        m_modules.add(module);
    }

    @Unbind
    public void unbindModule(Module module) {
        m_modules.remove(module);
    }

    /**
     * Creates a IPOJOURLHandler.
     * Gets the bundle context and create the working
     * directory.
     *
     * @param bundleContext the bundle context
     */
    public IPOJOURLHandler(BundleContext bundleContext) {
        this(bundleContext, bundleContext.getDataFile("temp"));
    }

    public IPOJOURLHandler(BundleContext context, File work) {
        m_context = context;
        m_temp = work;
        if (!m_temp.exists()) {
            m_temp.mkdir();
        }
    }

    /**
     * Stops the URL handler:
     * Deletes the working directory.
     */
    @Invalidate
    public void stop() {
        File[] files = m_temp.listFiles();
        if (files != null) {
            for (int i = 0; i < files.length; i++) {
                files[i].delete();
            }
        }
        m_temp.delete();
    }

    /**
     * Opens a connection using the ipojo url handler.
     * This methods parses the URL and manipulate the given bundle.
     *
     * @param url the url.
     * @return the URL connection on the manipulated bundle
     * @throws java.io.IOException occurs when the bundle cannot be either downloaded, or manipulated or
     *                             installed correctly.
     * @see org.osgi.service.url.AbstractURLStreamHandlerService#openConnection(java.net.URL)
     */
    public URLConnection openConnection(URL url) throws IOException {
        logger.log(LOG_DEBUG, format("Processing URL %s", url));
        String full = removeScheme(url);

        // Now full is like : URL!URL or URL
        String[] urls = full.split("!");
        URL bundleURL = null;
        URL metadataURL = null;
        if (urls.length == 1) {
            // URL form
            bundleURL = new URL(urls[0]);
        } else if (urls.length == 2) {
            // URL!URL form
            bundleURL = new URL(urls[0]);
            metadataURL = new URL(urls[1]);
        } else {
            throw new MalformedURLException("The iPOJO url is not formatted correctly, ipojo:bundle_url[!metadata_url] expected");
        }

        logger.log(LOG_DEBUG, format("Extracted URL %s", url));

        // Dump the referenced bundle on disk
        File original = File.createTempFile("original-", ".jar", m_temp);
        dump(bundleURL.openStream(), original);

        JarFile jf = new JarFile(original);

        File metadata = null;
        if (metadataURL != null) {
            metadata = File.createTempFile("ipojo-", ".xml", m_temp);
            dump(metadataURL, metadata);
        } else {
            // Check that the metadata are in the jar file
            metadata = findMetadata(jf);
        }

        Reporter reporter = new SystemReporter();
        File out = File.createTempFile("ipojo-", ".jar", m_temp);

        ResourceStore store = new BundleAwareJarFileResourceStore(jf, out, m_context);

        CompositeMetadataProvider composite = new CompositeMetadataProvider(reporter);
        if (metadata != null) {
            FileMetadataProvider provider = new FileMetadataProvider(metadata, reporter);
            composite.addMetadataProvider(provider);
        }

        ClassLoader classloader = new BridgeClassLoader(original, m_context);
        // Pojoization
        Pojoization pojoizator = new Pojoization(createModuleProvider());
        try {
            pojoizator.pojoization(store, composite, createVisitor(store, reporter), classloader);
        } catch (Exception e) {
            if (!pojoizator.getErrors().isEmpty()) {
                throw new IOException("Errors occurred during the manipulation : " + pojoizator.getErrors(), e);
            }
            e.printStackTrace();
            throw new IOException("Cannot manipulate the Url: " + url, e);
        }

        if (!pojoizator.getErrors().isEmpty()) {
            throw new IOException("Errors occurred during the manipulation : " + pojoizator.getErrors());
        }
        if (!pojoizator.getWarnings().isEmpty()) {
            logger.log(LOG_WARNING, format("Warnings occurred during the manipulation %s", pojoizator.getWarnings()));
        }

        logger.log(LOG_DEBUG, format("Manipulation done %s", out.exists()));

        // Cleanup
        if (metadata != null) {
            metadata.delete();
        }
        original.delete();
        out.deleteOnExit();

        // Returns the URL Connection
        return out.toURI().toURL().openConnection();


    }

    private String removeScheme(final URL url) {
        String full = url.toExternalForm();
        // Remove ipojo:
        if (full.startsWith(IPOJO_SCHEME)) {
            full = full.substring(IPOJO_SCHEME.length());
        }
        // Remove '/' or '//'
        while (full.startsWith("/")) {
            full = full.substring(1);
        }

        return full.trim();
    }

    private ManipulationVisitor createVisitor(ResourceStore store, Reporter reporter) {
        ManipulatedResourcesWriter writer = new ManipulatedResourcesWriter();
        writer.setReporter(reporter);
        writer.setResourceStore(store);

        CheckFieldConsistencyVisitor checkFieldConsistencyVisitor = new CheckFieldConsistencyVisitor(writer);
        checkFieldConsistencyVisitor.setReporter(reporter);
        return checkFieldConsistencyVisitor;
    }


    private ModuleProvider createModuleProvider() {
        return new CompositeModuleProvider(
                new CoreModuleProvider(),
                new DefaultModuleProvider(m_modules)
        );
    }

    /**
     * Looks for the metadata.xml file in the jar file.
     * Two locations are checked:
     * <ol>
     * <li>the root of the jar file</li>
     * <li>the META-INF directory</li>
     * </ol>
     *
     * @param jar the jar file
     * @return the found file or <code>null</code> if not found.
     * @throws java.io.IOException occurs when the Jar file cannot be read.
     */
    private File findMetadata(JarFile jar) throws IOException {
        JarEntry je = jar.getJarEntry("metadata.xml");
        if (je == null) {
            je = jar.getJarEntry("META-INF/metadata.xml");
        }

        if (je == null) {
            logger.log(LOG_DEBUG, "Metadata file not found, use annotations only.");
            return null; // Not Found, use annotation only
        } else {
            logger.log(LOG_DEBUG, format("Metadata file found at  %s", je.getName()));
            File metadata = File.createTempFile("ipojo-", ".xml", m_temp);
            dump(jar.getInputStream(je), metadata);
            logger.log(LOG_DEBUG, format("Metadata file saved at %s", metadata));
            return metadata;
        }

    }

}
TOP

Related Classes of org.apache.felix.ipojo.online.manipulator.IPOJOURLHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.