Package org.apache.sling.installer.factories.subsystems.impl

Source Code of org.apache.sling.installer.factories.subsystems.impl.SubsystemInstaller

/*
* 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.sling.installer.factories.subsystems.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.sling.installer.api.InstallableResource;
import org.apache.sling.installer.api.tasks.ChangeStateTask;
import org.apache.sling.installer.api.tasks.InstallTask;
import org.apache.sling.installer.api.tasks.InstallTaskFactory;
import org.apache.sling.installer.api.tasks.RegisteredResource;
import org.apache.sling.installer.api.tasks.ResourceState;
import org.apache.sling.installer.api.tasks.ResourceTransformer;
import org.apache.sling.installer.api.tasks.TaskResource;
import org.apache.sling.installer.api.tasks.TaskResourceGroup;
import org.apache.sling.installer.api.tasks.TransformationResult;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.Version;
import org.osgi.service.subsystem.Subsystem;
import org.osgi.service.subsystem.Subsystem.State;
import org.osgi.service.subsystem.SubsystemConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This is an extension for the OSGi installer
* It listens for files ending with ".esa" and a proper subsystem manifest.
* Though subsystems does not require a complete manifest, the installer supports
* only subsystems with the basic info (name and version).
*
* As subsystems currently do not support an update, an uninstall/install is done
* instead - which will lose bundle private data, bound configurations etc.
*/
public class SubsystemInstaller
    implements ResourceTransformer, InstallTaskFactory {

    private static final String TYPE_SUBSYSTEM = "esa";

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final Subsystem rootSubsystem;

    private final BundleContext bundleContext;

    public SubsystemInstaller(final Subsystem root, final BundleContext bundleContext) {
        this.rootSubsystem = root;
        this.bundleContext = bundleContext;
    }

    /**
     * @see org.apache.sling.installer.api.tasks.ResourceTransformer#transform(org.apache.sling.installer.api.tasks.RegisteredResource)
     */
    public TransformationResult[] transform(final RegisteredResource resource) {
        if ( resource.getType().equals(InstallableResource.TYPE_FILE) ) {
            if ( resource.getURL().endsWith("." + TYPE_SUBSYSTEM) ) {
                logger.info("Found potential subsystem resource {}", resource);
                final SubsystemInfo headers = readSubsystemHeaders(resource);
                if ( headers != null ) {
                    // check the version for validity
                    boolean validVersion = true;
                    try {
                        new Version(headers.version);
                    } catch (final IllegalArgumentException iae) {
                        logger.info("Rejecting subsystem {} from {} due to invalid version information: {}.",
                                new Object[] {headers.symbolicName, resource, headers.version});
                        validVersion = false;
                    }
                    if ( validVersion ) {
                        final Map<String, Object> attr = new HashMap<String, Object>();
                        attr.put(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME, headers.symbolicName);
                        attr.put(SubsystemConstants.SUBSYSTEM_VERSION, headers.version);

                        final TransformationResult tr = new TransformationResult();
                        tr.setId(headers.symbolicName);
                        tr.setResourceType(TYPE_SUBSYSTEM);
                        tr.setAttributes(attr);
                        tr.setVersion(new Version(headers.version));

                        return new TransformationResult[] {tr};
                    }

                } else {
                    logger.info("Subsystem resource does not have required headers.");
                }
            }
        }
        return null;
    }

    /**
     * Check that the required attributes are available.
     * This is just a sanity check
     */
    private SubsystemInfo checkResource(final TaskResourceGroup toActivate) {
        final TaskResource rsrc = toActivate.getActiveResource();

        SubsystemInfo result = null;
        final String symbolicName = (String) rsrc.getAttribute(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME);
        if ( symbolicName == null ) {
            logger.error("Subsystem resource is missing symbolic name {}", rsrc);
        } else {
            final String version = (String)rsrc.getAttribute(SubsystemConstants.SUBSYSTEM_VERSION);
            if ( version == null ) {
                logger.error("Subsystem resource is missing version {}", rsrc);
            } else {
                // check the version for validity
                boolean validVersion = true;
                try {
                    new Version(version);
                } catch (final IllegalArgumentException iae) {
                    logger.info("Rejecting subsystem {} from {} due to invalid version information: {}.",
                            new Object[] {symbolicName, rsrc, version});
                    validVersion = false;
                }
                if ( validVersion ) {
                    result = new SubsystemInfo();
                    result.symbolicName = symbolicName;
                    result.version = version;
                }
            }
        }
        return result;
    }

    private ServiceReference<Subsystem> getSubsystemReference(final String symbolicName) {
        // search a subsystem with the symbolic name
        ServiceReference<Subsystem> ref = null;
        try {
            final Collection<ServiceReference<Subsystem>> refs = this.bundleContext.getServiceReferences(Subsystem.class, "(subsystem.symbolicName=" + symbolicName + ")");
            if ( refs.size() > 0 ) {
                ref = refs.iterator().next();
            }
        } catch (final InvalidSyntaxException e) {
            logger.error("Problem searching for subsystem with symbolic name " + symbolicName, e);
        }
        return ref;
    }

    /**
     * @see org.apache.sling.installer.api.tasks.InstallTaskFactory#createTask(org.apache.sling.installer.api.tasks.TaskResourceGroup)
     */
    public InstallTask createTask(final TaskResourceGroup toActivate) {
        final InstallTask result;

        final TaskResource rsrc = toActivate.getActiveResource();
        if ( rsrc.getType().equals(TYPE_SUBSYSTEM) ) {

            // check if the required info is available
            final SubsystemInfo info = checkResource(toActivate);
            if ( info == null ) {
                // ignore as info is missing
                result = new ChangeStateTask(toActivate, ResourceState.IGNORED);
            } else {
                // search a subsystem with the symbolic name
                final ServiceReference<Subsystem> ref = this.getSubsystemReference(info.symbolicName);

                final Subsystem currentSubsystem = (ref != null ? this.bundleContext.getService(ref) : null);
                try {
                    final Version newVersion = new Version(info.version);
                    final Version oldVersion = (ref == null ? null : (Version)ref.getProperty("subsystem.version"));

                    // Install
                    if ( rsrc.getState() == ResourceState.INSTALL ) {
                        if ( oldVersion != null ) {

                            final int compare = oldVersion.compareTo(newVersion);
                            if (compare < 0) {
                                // installed version is lower -> update
                                result = new UpdateSubsystemTask(toActivate, this.bundleContext, ref, this.rootSubsystem);
                            } else if ( compare == 0 && isSnapshot(newVersion) ) {
                                // same version but snapshot -> update
                                result = new UpdateSubsystemTask(toActivate, this.bundleContext, ref, this.rootSubsystem);
                            } else if ( compare == 0 && currentSubsystem != null && currentSubsystem.getState() != State.ACTIVE ) {
                                // try to start the version
                                result = new StartSubsystemTask(toActivate, currentSubsystem);
                            } else {
                                logger.info("{} is not installed, subsystem with same or higher version is already installed: {}", info, newVersion);
                                result = new ChangeStateTask(toActivate, ResourceState.IGNORED);
                            }
                        } else {
                            result = new InstallSubsystemTask(toActivate, this.rootSubsystem);
                        }

                    // Uninstall
                    } else if ( rsrc.getState() == ResourceState.UNINSTALL ) {
                        if ( oldVersion == null ) {
                            logger.error("Nothing to uninstall. {} is currently not installed.", info);
                            result = new ChangeStateTask(toActivate, ResourceState.IGNORED);
                        } else {

                            final int compare = oldVersion.compareTo(newVersion);
                            if ( compare == 0 ) {
                                result = new UninstallSubsystemTask(toActivate, this.bundleContext, ref);
                            } else {
                                logger.error("Nothing to uninstall. {} is currently not installed, different version is installed {}", info, oldVersion);
                                result = new ChangeStateTask(toActivate, ResourceState.IGNORED);
                            }
                        }
                    } else {
                        result = null;
                    }
                } finally {
                    if ( currentSubsystem != null ) {
                        this.bundleContext.ungetService(ref);
                    }
                }
            }
        } else {
            result = null;
        }
        return result;
    }

    /**
     * Read the manifest from supplied input stream, which is closed before return.
     */
    private static Manifest getManifest(final RegisteredResource rsrc, final Logger logger)
    throws IOException {
        final InputStream ins = rsrc.getInputStream();

        Manifest result = null;

        if ( ins != null ) {
            ZipInputStream jis = null;
            try {
                jis = new ZipInputStream(ins);

                ZipEntry entry;

                while ( (entry = jis.getNextEntry()) != null ) {
                    if (entry.getName().equals("OSGI-INF/SUBSYSTEM.MF") ) {
                        result = new Manifest(jis);
                    }
                }

            } finally {

                // close the jar stream or the input stream, if the jar
                // stream is set, we don't need to close the input stream
                // since closing the jar stream closes the input stream
                if (jis != null) {
                    try {
                        jis.close();
                    } catch (IOException ignore) {
                    }
                } else {
                    try {
                        ins.close();
                    } catch (IOException ignore) {
                    }
                }
            }
        }
        return result;
    }

    final static public class SubsystemInfo {
        public String symbolicName;
        public String version;
        @Override

        public String toString() {
            return "Subsystem[symbolicName=" + symbolicName + ", version="
                    + version + "]";
        }

    }

    /**
     * Read the subsystem info from the manifest (if available)
     */
    private SubsystemInfo readSubsystemHeaders(final RegisteredResource resource) {
        try {
            final Manifest m = SubsystemInstaller.getManifest(resource, logger);
            if (m != null) {
                final String sn = m.getMainAttributes().getValue(SubsystemConstants.SUBSYSTEM_SYMBOLICNAME);
                if (sn != null) {
                    final String v = m.getMainAttributes().getValue(SubsystemConstants.SUBSYSTEM_VERSION);
                    final int paramPos = sn.indexOf(';');
                    final String symbolicName = (paramPos == -1 ? sn : sn.substring(0, paramPos));
                    final SubsystemInfo headers = new SubsystemInfo();
                    headers.symbolicName = symbolicName;
                    headers.version = v;

                    // if no version is specified, use default version
                    if ( headers.version == null ) {
                        headers.version = "0.0.0.0";
                    }
                    return headers;
                }
            }
        } catch (final IOException ignore) {
            // ignore
        }
        return null;
    }

    private static final String MAVEN_SNAPSHOT_MARKER = "SNAPSHOT";

    /**
     * Check if the version is a snapshot version
     */
    public static boolean isSnapshot(final Version v) {
        return v.toString().indexOf(MAVEN_SNAPSHOT_MARKER) >= 0;
    }
}
TOP

Related Classes of org.apache.sling.installer.factories.subsystems.impl.SubsystemInstaller

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.