This is an automated email from the ASF dual-hosted git repository. rombert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-factory-packages.git
commit ebf980aaa8c0a04a3205a4b52da639bb9488cc8a Author: Carsten Ziegeler <[email protected]> AuthorDate: Fri Oct 7 08:40:29 2016 +0000 SLING-6082 : Add installer factory for content packages git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1763696 13f79535-47bb-0310-9956-ffa450edef68 --- pom.xml | 87 +++++ .../factory/packages/impl/PackageTransformer.java | 367 +++++++++++++++++++++ 2 files changed, 454 insertions(+) diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..5fb25fd --- /dev/null +++ b/pom.xml @@ -0,0 +1,87 @@ +<?xml version="1.0"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.sling</groupId> + <artifactId>sling</artifactId> + <version>28</version> + <relativePath/> + </parent> + + <artifactId>org.apache.sling.installer.factory.packages</artifactId> + <version>0.1.0-SNAPSHOT</version> + <packaging>bundle</packaging> + + <name>Apache Sling Installer Content Package Support</name> + <description> + Provides support for content packages to the Apache Sling OSGi installer + </description> + + <scm> + <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/installer/factories/packages</connection> + <developerConnection> scm:svn:https://svn.apache.org/repos/asf/sling/trunk/installer/factories/packages</developerConnection> + <url>http://svn.apache.org/viewvc/sling/trunk/installer/factories/packages</url> + </scm> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>osgi.core</artifactId> + </dependency> + <dependency> + <groupId>javax.jcr</groupId> + <artifactId>jcr</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.jcr.api</artifactId> + <version>2.1.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.installer.core</artifactId> + <version>3.5.0</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>org.apache.jackrabbit.vault</groupId> + <artifactId>org.apache.jackrabbit.vault</artifactId> + <version>3.1.30</version> + <scope>provided</scope> + </dependency> + </dependencies> + +</project> diff --git a/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformer.java b/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformer.java new file mode 100644 index 0000000..d46c905 --- /dev/null +++ b/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformer.java @@ -0,0 +1,367 @@ +/* + * 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.factory.packages.impl; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.zip.ZipInputStream; + +import javax.jcr.Session; + +import org.apache.jackrabbit.vault.fs.io.ImportOptions; +import org.apache.jackrabbit.vault.packaging.Dependency; +import org.apache.jackrabbit.vault.packaging.JcrPackage; +import org.apache.jackrabbit.vault.packaging.JcrPackageManager; +import org.apache.jackrabbit.vault.packaging.PackageId; +import org.apache.jackrabbit.vault.packaging.Packaging; +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.InstallationContext; +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.RetryHandler; +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.apache.sling.jcr.api.SlingRepository; +import org.osgi.framework.Version; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * The package transformer: + * <ul> + * <li>detects content packages (ResourceTransformer) + * <li>and creates tasks for installing / removing of content packages + * </ul> + */ +@Component( service = {ResourceTransformer.class, InstallTaskFactory.class}) +public class PackageTransformer implements ResourceTransformer, InstallTaskFactory { + + /** The attribute holding the package id. */ + private static final String ATTR_PCK_ID = "package-id"; + + /** The resource type for packages. */ + private static final String RESOURCE_TYPE = "content-package"; + + /** + * The logger. + */ + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Reference + private SlingRepository repository; + + @Reference + private Packaging pkgSvc; + + @Reference + private RetryHandler retryHandler; + + /** + * @see org.apache.sling.installer.api.tasks.ResourceTransformer#transform(org.apache.sling.installer.api.tasks.RegisteredResource) + */ + @Override + public TransformationResult[] transform(final RegisteredResource resource) { + if (resource.getType().equals(InstallableResource.TYPE_FILE)) { + return checkForPackage(resource); + } + return null; + } + + /** + * Check if the resource is a content package + * @param resource The resource + * @return {@code null} if not a content package, a result otherwise + */ + private TransformationResult[] checkForPackage(final RegisteredResource resource) { + // first check if this is a zip archive + try (final ZipInputStream zin = new ZipInputStream(new BufferedInputStream(resource.getInputStream()))) { + if (zin.getNextEntry() == null) { + return null; + } + } catch (final IOException ioe) { + logger.debug("Unable to read resource.", ioe); + return null; + } + + Session session = null; + JcrPackage pck = null; + try { + // create an admin session + session = repository.loginAdministrative(null); + + final JcrPackageManager pckMgr = pkgSvc.getPackageManager(session); + pck = pckMgr.upload(resource.getInputStream(), true, true); + if (pck.isValid()) { + final PackageId pid = pck.getDefinition().getId(); + final Map<String, Object> attrs = new HashMap<String, Object>(); + attrs.put(ATTR_PCK_ID, pid.toString()); + + final TransformationResult tr = new TransformationResult(); + tr.setId(pid.getGroup() + ':' + pid.getName()); + tr.setResourceType(RESOURCE_TYPE); + tr.setAttributes(attrs); + + // version + final String version = pid.getVersionString(); + if ( version.length() > 0 ) { + tr.setVersion(new Version(cleanupVersion(version))); + } + + return new TransformationResult[] {tr}; + } + } catch (final Exception ioe) { + logger.debug("Unable to check content package " + resource.getURL(), ioe); + } finally { + if (pck != null) { + pck.close(); + } + if (session != null) { + session.logout(); + } + } + return null; + } + + /** + * @see org.apache.sling.installer.api.tasks.InstallTaskFactory#createTask(org.apache.sling.installer.api.tasks.TaskResourceGroup) + */ + @Override + public InstallTask createTask(final TaskResourceGroup toActivate) { + final TaskResource resource = toActivate.getActiveResource(); + if (resource == null || !resource.getType().equals(RESOURCE_TYPE)) { + return null; + } + + // extract the package id + final String id = (String)resource.getAttribute(ATTR_PCK_ID); + final PackageId pkgId = PackageId.fromString(id); + if (pkgId == null) { + logger.error("Error during processing of {}: Package id is wrong/null.", resource); + return new ChangeStateTask(toActivate, ResourceState.IGNORED); + } + + if (resource.getState() == ResourceState.INSTALL) { + return new InstallPackageTask(pkgId, toActivate); + } + return new UninstallPackageTask(pkgId, toActivate); + } + + /** + * Task for installing a package. + */ + private class InstallPackageTask extends InstallTask { + + private final PackageId pkgId; + + public InstallPackageTask(final PackageId pkgId, final TaskResourceGroup erl) { + super(erl); + this.pkgId = pkgId; + } + + @Override + public void execute(final InstallationContext ctx) { + final TaskResource resource = this.getResource(); + + // now check the dependencies + Session session = null; + JcrPackage pkg = null; + try { + session = repository.loginAdministrative(null); + final JcrPackageManager pkgMgr = pkgSvc.getPackageManager(session); + + // open package + pkg = pkgMgr.open(pkgId); + if (pkg == null) { + logger.error("Error during installation of {}: Package {} missing.", resource, pkgId); + this.setFinishedState(ResourceState.IGNORED); + return; + } + + // check if package was installed in the meantime + if (pkg.isInstalled()) { + logger.info("Package {} was installed externally. Marking as installed.", pkgId); + this.setFinishedState(ResourceState.INSTALLED); + return; + } + + // check if dependencies are installed + for (final Dependency d : pkg.getDefinition().getDependencies()) { + if (pkgMgr.resolve(d, true) == null) { + logger.info("Delaying installation of {} due to missing dependency {}.", pkgId, d); + return; + } + } + + // finally, install package + final ImportOptions opts = new ImportOptions(); + + pkg.install(opts); + + ctx.log("Content package installed: {}", resource); + setFinishedState(ResourceState.INSTALLED); + + // notify retry handler to install dependend packages. + retryHandler.scheduleRetry(); + + } catch (final Exception e) { + logger.error("Error while processing install task of {}.", resource, e); + this.setFinishedState(ResourceState.IGNORED); + } finally { + if (pkg != null) { + pkg.close(); + } + if (session != null) { + session.logout(); + } + } + } + + @Override + public String getSortKey() { + return "25-" + getResource().getEntityId(); + } + } + + /** + * Task for uninstalling a package. + */ + private final class UninstallPackageTask extends InstallTask { + + private final PackageId pkgId; + + public UninstallPackageTask(final PackageId pkgId, final TaskResourceGroup erl) { + super(erl); + this.pkgId = pkgId; + } + + @Override + public void execute(final InstallationContext ctx) { + Session session = null; + JcrPackage pkg = null; + try { + session = repository.loginAdministrative(null); + final JcrPackageManager pkgMgr = pkgSvc.getPackageManager(session); + + pkg = pkgMgr.open(this.pkgId); + if ( pkg != null ) { + final ImportOptions opts = new ImportOptions(); + pkg.uninstall(opts); + } + + } catch (final Exception e) { + logger.error("Error while processing uninstall task of {}.", pkgId, e); + } finally { + if (pkg != null) { + pkg.close(); + } + if (session != null) { + session.logout(); + } + } + ctx.log("Uninstalled content package {}", getResource()); + setFinishedState(ResourceState.UNINSTALLED); + retryHandler.scheduleRetry(); + } + + @Override + public String getSortKey() { + return "55-" + getResource().getEntityId(); + } + } + + private static final Pattern FUZZY_VERSION = Pattern.compile( "(\\d+)(\\.(\\d+)(\\.(\\d+))?)?([^a-zA-Z0-9](.*))?", + Pattern.DOTALL ); + + /** + * Clean up version parameters. Other builders use more fuzzy definitions of + * the version syntax. This method cleans up such a version to match an OSGi + * version. + * + * @param version The version string to clean up + * @return the clean version + */ + private static String cleanupVersion(final String version) { + final StringBuilder result = new StringBuilder(); + final Matcher m = FUZZY_VERSION.matcher( version ); + if ( m.matches() ) { + final String major = m.group( 1 ); + final String minor = m.group( 3 ); + final String micro = m.group( 5 ); + final String qualifier = m.group( 7 ); + + if ( major != null ) { + result.append( major ); + if ( minor != null ) { + result.append( "." ); + result.append( minor ); + if ( micro != null ) { + result.append( "." ); + result.append( micro ); + if ( qualifier != null ) { + result.append( "." ); + cleanupModifier( result, qualifier ); + } + } else if ( qualifier != null ) { + result.append( ".0." ); + cleanupModifier( result, qualifier ); + } else { + result.append( ".0" ); + } + } else if ( qualifier != null ) { + result.append( ".0.0." ); + cleanupModifier( result, qualifier ); + } else { + result.append( ".0.0" ); + } + } + } else { + result.append( "0.0.0." ); + cleanupModifier( result, version ); + } + return result.toString(); + } + + + private static void cleanupModifier( final StringBuilder result, final String modifier ) { + for ( int i = 0; i < modifier.length(); i++ ) { + char c = modifier.charAt( i ); + if ( ( c >= '0' && c <= '9' ) + || ( c >= 'a' && c <= 'z' ) + || ( c >= 'A' && c <= 'Z' ) + || c == '_' + || c == '-' ) { + result.append( c ); + } else { + result.append( '_' ); + } + } + } +} -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
