Package org.apache.felix.deploymentadmin.itest.util

Source Code of org.apache.felix.deploymentadmin.itest.util.DeploymentPackageBuilder$JarManifestManipulatingFilter

/*
* 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.deploymentadmin.itest.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;

import org.osgi.framework.Version;

/**
* Builder for deployment packages. Can handle bundles, resource processors and artifacts.
*/
public class DeploymentPackageBuilder {

    /**
     * Convenience resource filter for manipulating JAR manifests.
     */
    public abstract static class JarManifestFilter implements ResourceFilter {

        public final InputStream createInputStream(URL url) throws IOException {
            byte[] buffer = new byte[BUFFER_SIZE];

            JarInputStream jis = new JarInputStream(url.openStream());
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            JarOutputStream jos = new JarOutputStream(baos, filterManifest(jis.getManifest()));

            JarEntry input;
            while ((input = jis.getNextJarEntry()) != null) {
                jos.putNextEntry(input);
                int read;
                while ((read = jis.read(buffer)) > 0) {
                    jos.write(buffer, 0, read);
                }
                jos.closeEntry();
            }
            jos.close();
            jis.close();

            return new ByteArrayInputStream(baos.toByteArray());
        }
       
        protected abstract Manifest filterManifest(Manifest manifest);
    }
   
    /**
     * Simple manifest JAR manipulator implementation.
     */
    public static class JarManifestManipulatingFilter extends JarManifestFilter {
        private final String[] m_replacementEntries;
       
        public JarManifestManipulatingFilter(String... replacementEntries) {
            if (replacementEntries == null || ((replacementEntries.length) % 2 != 0)) {
                throw new IllegalArgumentException("Entries must be a multiple of two!");
            }
            m_replacementEntries = Arrays.copyOf(replacementEntries, replacementEntries.length);
        }

        @Override
        protected Manifest filterManifest(Manifest manifest) {
            for (int i = 0; i < m_replacementEntries.length; i += 2) {
                String key = m_replacementEntries[i];
                String value = m_replacementEntries[i+1];
                manifest.getMainAttributes().putValue(key, value);
            }
            return manifest;
        }
    }
   
    private static final int BUFFER_SIZE = 32 * 1024;

    private final String m_symbolicName;
    private final String m_version;
    private final List<ArtifactData> m_bundles = new ArrayList<ArtifactData>();
    private final List<ArtifactData> m_processors = new ArrayList<ArtifactData>();

    private final List<ArtifactData> m_artifacts = new ArrayList<ArtifactData>();
    private String m_fixPackageVersion;

    private boolean m_verification;

    private DeploymentPackageBuilder(String symbolicName, String version) {
        m_symbolicName = symbolicName;
        m_version = version;
       
        m_verification = true;
    }

    /**
     * Creates a new deployment package builder.
     *
     * @param name the name of the deployment package
     * @param version the version of the deployment package
     * @return a builder to further add data to the deployment package
     */
    public static DeploymentPackageBuilder create(String name, String version) {
        return new DeploymentPackageBuilder(name, version);
    }

    /**
     * Adds an artifact to the deployment package.
     *
     * @param builder the artifact data builder to use.
     * @return this builder.
     * @throws Exception if something goes wrong while building the artifact.
     */
    public DeploymentPackageBuilder add(ArtifactDataBuilder builder) throws Exception {
        ArtifactData artifactData = builder.build();
        if (artifactData.isCustomizer()) {
            m_processors.add(artifactData);
        }
        else if (artifactData.isBundle()) {
            m_bundles.add(artifactData);
        }
        else {
            m_artifacts.add(artifactData);
        }
        return this;
    }

    /**
     * Creates a new deployment package builder with the same symbolic name as this builder.
     *
     * @param name the name of the deployment package
     * @param version the version of the deployment package
     * @return a builder to further add data to the deployment package
     */
    public DeploymentPackageBuilder create(String version) {
        return new DeploymentPackageBuilder(getSymbolicName(), version);
    }

    public BundleDataBuilder createBundleResource() {
        return new BundleDataBuilder();
    }

    public ResourceDataBuilder createResource() {
        return new ResourceDataBuilder();
    }

    public ResourceProcessorDataBuilder createResourceProcessorResource() {
        return new ResourceProcessorDataBuilder();
    }

    /**
     * Disables the verification of the generated deployment package, potentially causing an erroneous result to be generated.
     * 
     * @return this builder.
     */
    public DeploymentPackageBuilder disableVerification() {
        m_verification = false;
        return this;
    }

    /**
     * Generates a deployment package and streams it to the output stream you provide. Before
     * it starts generating, it will first validate that you have actually specified a
     * resource processor for each type of artifact you provided.
     *
     * @return the input stream containing the deployment package.
     * @throws Exception if something goes wrong while validating or generating
     */
    public InputStream generate() throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        generate(baos);

        return new ByteArrayInputStream(baos.toByteArray());
    }

    /**
     * Generates a deployment package and streams it to the output stream you provide. Before
     * it starts generating, it will first validate that you have actually specified a
     * resource processor for each type of artifact you provided.
     *
     * @param output the output stream to write to
     * @throws Exception if something goes wrong while validating or generating
     */
    public void generate(OutputStream output) throws Exception {
        List<ArtifactData> artifacts = new ArrayList<ArtifactData>();
        artifacts.addAll(m_bundles);
        artifacts.addAll(m_processors);
        artifacts.addAll(m_artifacts);
       
        if (m_verification) {
            validateProcessedArtifacts();
            validateMissingArtifacts(artifacts);
        }
       
        Manifest m = createManifest(artifacts);
        writeStream(artifacts, m, output);
    }

    /**
     * @return the symbolic name of the deployment package.
     */
    public String getSymbolicName() {
        return m_symbolicName;
    }

    /**
     * @return the version of the deployment package.
     */
    public String getVersion() {
        return m_version;
    }

    /**
     * Marks this deployment package as a 'fix' package.
     *
     * @return this builder.
     */
    public DeploymentPackageBuilder setFixPackage() {
        Version v1 = new Version(m_version);
        Version v2 = new Version(v1.getMajor() + 1, 0, 0);
        String version = String.format("[%d.%d, %d.%d)", v1.getMajor(), v1.getMinor(), v2.getMajor(), v2.getMinor());
        return setFixPackage(version);
    }

    /**
     * Marks this deployment package as a 'fix' package.
     *
     * @param versionRange the version range in which this fix-package should be applied.
     * @return this builder.
     */
    public DeploymentPackageBuilder setFixPackage(String versionRange) {
        m_fixPackageVersion = versionRange;
        return this;
    }

    private Manifest createManifest(List<ArtifactData> files) throws Exception {
        Manifest manifest = new Manifest();
        Attributes main = manifest.getMainAttributes();
        main.putValue("Manifest-Version", "1.0");
        main.putValue("DeploymentPackage-SymbolicName", m_symbolicName);
        main.putValue("DeploymentPackage-Version", m_version);

        if ((m_fixPackageVersion != null) && !"".equals(m_fixPackageVersion)) {
            main.putValue("DeploymentPackage-FixPack", m_fixPackageVersion);
        }

        Map<String, Attributes> entries = manifest.getEntries();

        Iterator<ArtifactData> filesIter = files.iterator();
        while (filesIter.hasNext()) {
            ArtifactData file = filesIter.next();

            Attributes a = new Attributes();
            a.putValue("Name", file.getFilename());

            if (file.isBundle()) {
                a.putValue("Bundle-SymbolicName", file.getSymbolicName());
                a.putValue("Bundle-Version", file.getVersion());
                if (file.isCustomizer()) {
                    a.putValue("DeploymentPackage-Customizer", "true");
                    a.putValue("Deployment-ProvidesResourceProcessor", file.getProcessorPid());
                }
            }
            else {
                a.putValue("Resource-Processor", file.getProcessorPid());
            }

            if (file.isMissing()) {
                a.putValue("DeploymentPackage-Missing", "true");
            }

            entries.put(file.getFilename(), a);
        }

        return manifest;
    }
   
    private void validateMissingArtifacts(List<ArtifactData> files) throws Exception {
        boolean missing = false;
       
        Iterator<ArtifactData> artifactIter = files.iterator();
        while (artifactIter.hasNext() && !missing) {
            ArtifactData data = artifactIter.next();
           
            if (data.isMissing()) {
                missing = true;
            }
        }
       
        if (missing && (m_fixPackageVersion == null || "".equals(m_fixPackageVersion))) {
            throw new Exception("Artifact cannot be missing without a fix package version!");
        }
    }

    private void validateProcessedArtifacts() throws Exception {
        Iterator<ArtifactData> artifactIter = m_artifacts.iterator();
        while (artifactIter.hasNext()) {
            ArtifactData data = artifactIter.next();
            String pid = data.getProcessorPid();
            boolean found = false;

            Iterator<ArtifactData> processorIter = m_processors.iterator();
            while (processorIter.hasNext()) {
                ArtifactData processor = processorIter.next();
                if (pid.equals(processor.getProcessorPid())) {
                    found = true;
                    break;
                }
            }

            if (!found) {
                throw new Exception("No resource processor found for artifact " + data.getURL()
                    + " with processor PID " + pid);
            }
        }
    }

    private void writeStream(List<ArtifactData> files, Manifest manifest, OutputStream outputStream) throws Exception {
        JarOutputStream output = null;
        InputStream fis = null;
        try {
            output = new JarOutputStream(outputStream, manifest);
            byte[] buffer = new byte[BUFFER_SIZE];

            Iterator<ArtifactData> filesIter = files.iterator();
            while (filesIter.hasNext()) {
                ArtifactData file = filesIter.next();
                if (file.isMissing()) {
                    // No need to write the 'missing' files...
                    continue;
                }

                output.putNextEntry(new JarEntry(file.getFilename()));

                ResourceFilter filter = file.getFilter();
                if (filter != null) {
                    fis = filter.createInputStream(file.getURL());
                }
                else {
                    fis = file.getURL().openStream();
                }

                try {
                    int bytes = fis.read(buffer);
                    while (bytes != -1) {
                        output.write(buffer, 0, bytes);
                        bytes = fis.read(buffer);
                    }
                }
                finally {
                    fis.close();
                    fis = null;

                    output.closeEntry();
                }
            }
        }
        finally {
            if (fis != null) {
                fis.close();
            }
            if (output != null) {
                output.close();
            }
        }
    }
}
TOP

Related Classes of org.apache.felix.deploymentadmin.itest.util.DeploymentPackageBuilder$JarManifestManipulatingFilter

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.