Author: kwin
Date: Fri Jun 30 14:16:53 2017
New Revision: 1800415
URL: http://svn.apache.org/viewvc?rev=1800415&view=rev
Log:
SLING-6987 initial support for hollow packages added
Modified:
sling/trunk/installer/factories/packages/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformer.java
sling/trunk/installer/factories/packages/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformerConfiguration.java
Modified:
sling/trunk/installer/factories/packages/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformer.java
URL:
http://svn.apache.org/viewvc/sling/trunk/installer/factories/packages/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformer.java?rev=1800415&r1=1800414&r2=1800415&view=diff
==============================================================================
---
sling/trunk/installer/factories/packages/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformer.java
(original)
+++
sling/trunk/installer/factories/packages/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformer.java
Fri Jun 30 14:16:53 2017
@@ -21,19 +21,27 @@ package org.apache.sling.installer.facto
import java.io.BufferedInputStream;
import java.io.IOException;
import java.text.MessageFormat;
+import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;
+import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipInputStream;
+import javax.jcr.RepositoryException;
import javax.jcr.Session;
+import org.apache.jackrabbit.util.ISO8601;
+import org.apache.jackrabbit.vault.fs.io.Archive;
import org.apache.jackrabbit.vault.fs.io.ImportOptions;
+import org.apache.jackrabbit.vault.fs.io.ZipStreamArchive;
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.PackageException;
import org.apache.jackrabbit.vault.packaging.PackageId;
+import org.apache.jackrabbit.vault.packaging.PackageProperties;
import org.apache.jackrabbit.vault.packaging.Packaging;
import org.apache.sling.installer.api.InstallableResource;
import org.apache.sling.installer.api.tasks.ChangeStateTask;
@@ -52,30 +60,29 @@ import org.osgi.framework.Version;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-
-/**
- * The package transformer:
+/** The package transformer:
* <ul>
- * <li>detects content packages (ResourceTransformer)
- * <li>and creates tasks for installing / removing of content packages
+ * <li>detects content packages (ResourceTransformer)
+ * <li>and creates tasks for installing / removing of content packages
* </ul>
- */
-@Component( service = {ResourceTransformer.class, InstallTaskFactory.class})
+*/
+@Component(service = { ResourceTransformer.class, InstallTaskFactory.class })
+@Designate(ocd=PackageTransformerConfiguration.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 resource types for packages. */
+ private static final String RESOURCE_TYPE_REGULAR = "content-package";
+ private static final String RESOURCE_TYPE_HOLLOW =
"content-package-hollow";
- /**
- * The logger.
- */
- private final Logger logger = LoggerFactory.getLogger(this.getClass());
+ /** The logger. */
+ private static final Logger logger =
LoggerFactory.getLogger(PackageTransformer.class);
@Reference
private SlingRepository repository;
@@ -93,9 +100,7 @@ public class PackageTransformer implemen
this.configuration = configuration;
}
- /**
- * @see
org.apache.sling.installer.api.tasks.ResourceTransformer#transform(org.apache.sling.installer.api.tasks.RegisteredResource)
- */
+ /** @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)) {
@@ -104,11 +109,10 @@ public class PackageTransformer implemen
return null;
}
- /**
- * Check if the resource is a content package
+ /** Check if the resource is a content package
+ *
* @param resource The resource
- * @return {@code null} if not a content package, a result otherwise
- */
+ * @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()))) {
@@ -127,25 +131,34 @@ public class PackageTransformer implemen
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)));
+ final TransformationResult tr = new TransformationResult();
+ if (configuration.shouldCreateHollowPackages()) {
+ tr.setResourceType(RESOURCE_TYPE_HOLLOW);
+ // TODO: getting the real package id, currently this requires
opening and extracting the archive (and copying files)
+ // Solved with https://issues.apache.org/jira/browse/JCRVLT-187
+ // for now we just take the filename from the URL
+ tr.setId(extractNameFromUrl(resource.getURL()));
+ } else {
+ 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());
+ tr.setId(pid.getGroup() + ':' + pid.getName());
+ tr.setResourceType(RESOURCE_TYPE_REGULAR);
+ tr.setAttributes(attrs);
+
+ // version
+ final String version = pid.getVersionString();
+ if (version.length() > 0) {
+ tr.setVersion(new Version(cleanupVersion(version)));
+ }
+ } else {
+ logger.warn("Package from resource {} is invalid",
resource);
+ return null;
}
-
- return new TransformationResult[] {tr};
}
+ return new TransformationResult[] { tr };
} catch (final Exception ioe) {
logger.debug("Unable to check content package " +
resource.getURL(), ioe);
} finally {
@@ -160,39 +173,75 @@ public class PackageTransformer implemen
}
/**
- * @see
org.apache.sling.installer.api.tasks.InstallTaskFactory#createTask(org.apache.sling.installer.api.tasks.TaskResourceGroup)
+ * Converts all separators to the Unix separator of forward slash.
+ *
+ * @param path the path to be changed, null ignored
+ * @return the updated path
*/
+ private static String separatorsToUnix(String path) {
+ if (path == null || path.indexOf('\\') == -1) {
+ return path;
+ }
+ return path.replace('\\', '/');
+ }
+
+ private static String extractNameFromUrl(String url) {
+ String lastIdPart = separatorsToUnix(url);;
+ final int pos = lastIdPart.lastIndexOf('/');
+ if ( pos != -1 ) {
+ lastIdPart = lastIdPart.substring(pos + 1);
+ }
+ return lastIdPart;
+ }
+
+ /** @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)) {
+ if (resource == null) {
+ logger.warn("The given resource to createTask is null");
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);
+ InstallTask task = null;
+ switch (resource.getType()) {
+ case RESOURCE_TYPE_REGULAR:
+ // 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) {
+ task = new InstallPackageTask(pkgId, toActivate);
+ } else {
+ task = new UninstallPackageTask(pkgId, toActivate);
+ }
+ break;
+ case RESOURCE_TYPE_HOLLOW:
+ if (resource.getState() == ResourceState.INSTALL) {
+ task = new InstallHollowPackageTask(toActivate);
+ } else {
+ // most probably uninstallation is not successful because this
has also been installed as hollow-package!
+ logger.info("Do not uninstall {}: Hollow-Packages cannot be
uninstalled.", resource);
+ return new ChangeStateTask(toActivate, ResourceState.IGNORED);
+ }
+ break;
+ default:
+ logger.error("Unupported type of {}: {}.", resource,
resource.getType());
+ task = new ChangeStateTask(toActivate, ResourceState.IGNORED);
}
- return new UninstallPackageTask(pkgId, toActivate);
+ return task;
}
- /**
- * Task for installing a package.
- */
- private class InstallPackageTask extends InstallTask {
+ private abstract class AbstractPackageInstallTask extends InstallTask {
- private final PackageId pkgId;
+ protected final String name;
- public InstallPackageTask(final PackageId pkgId, final
TaskResourceGroup erl) {
+ public AbstractPackageInstallTask(final String name, final
TaskResourceGroup erl) {
super(erl);
- this.pkgId = pkgId;
+ this.name = name;
}
@Override
@@ -206,8 +255,42 @@ public class PackageTransformer implemen
session = repository.loginAdministrative(null);
final JcrPackageManager pkgMgr =
pkgSvc.getPackageManager(session);
- // open package
- pkg = pkgMgr.open(pkgId);
+ doExecute(ctx, pkgMgr, resource);
+ } catch (final Exception e) {
+ String message = MessageFormat.format("Error while processing
{0} content package task of {1} due to {2}, no retry.", name, resource,
+ e.getLocalizedMessage());
+ logger.error(message, e);
+ this.setFinishedState(ResourceState.IGNORED, null, message);
+ } finally {
+ if (pkg != null) {
+ pkg.close();
+ }
+ if (session != null) {
+ session.logout();
+ }
+ }
+ }
+
+ protected abstract void doExecute(final InstallationContext ctx, final
JcrPackageManager pkgMgr, final TaskResource resource)
+ throws RepositoryException, PackageException, IOException;
+ }
+
+ /** Task for installing a package. */
+ private class InstallPackageTask extends AbstractPackageInstallTask {
+ private final PackageId pkgId;
+
+ public InstallPackageTask(final PackageId pkgId, final
TaskResourceGroup erl) {
+ super("install", erl);
+ this.pkgId = pkgId;
+ }
+
+ @Override
+ protected void doExecute(final InstallationContext ctx, final
JcrPackageManager pkgMgr, final TaskResource resource)
+ throws RepositoryException, PackageException, IOException {
+
+ // open package
+ JcrPackage pkg = pkgMgr.open(pkgId);
+ try {
if (pkg == null) {
String message = MessageFormat.format("Error during
installation of {0}: Package {1} missing.", resource, pkgId);
logger.error(message);
@@ -241,23 +324,15 @@ public class PackageTransformer implemen
ctx.log("Content package extracted: {}", resource);
}
-
setFinishedState(ResourceState.INSTALLED);
- // notify retry handler to install dependend packages.
+ // notify retry handler to install dependent packages.
retryHandler.scheduleRetry();
- } catch (final Exception e) {
- String message = MessageFormat.format("Error while processing
install task of {0} due to {1}, no retry.", resource, e.getLocalizedMessage());
- logger.error(message, e);
- this.setFinishedState(ResourceState.IGNORED, null, message);
} finally {
if (pkg != null) {
pkg.close();
}
- if (session != null) {
- session.logout();
- }
}
}
@@ -267,117 +342,133 @@ public class PackageTransformer implemen
}
}
- /**
- * Task for uninstalling a package.
- */
- private final class UninstallPackageTask extends InstallTask {
+ private final class InstallHollowPackageTask extends
AbstractPackageInstallTask {
- private final PackageId pkgId;
+ public InstallHollowPackageTask(TaskResourceGroup erl) {
+ super("install-hollow", erl);
+ }
+
+ @Override
+ protected void doExecute(InstallationContext ctx, JcrPackageManager
pkgMgr, TaskResource resource)
+ throws RepositoryException, PackageException, IOException {
+ Archive archive = new ZipStreamArchive(resource.getInputStream());
+ JcrPackage oldPackage = null;
+ try {
+ archive.open(false);
+ // we always have to do that, as there is no possibility to
figure out whether the same package has already been installed
+ // https://issues.apache.org/jira/browse/JCRVLT-188
+ final ImportOptions opts = new ImportOptions();
+ pkgMgr.extract(archive, opts, true);
+ } finally {
+ archive.close();
+ if (oldPackage != null) {
+ oldPackage.close();
+ }
+ }
+ }
+
+ @Override
+ public String getSortKey() {
+ return "35-" + getResource().getEntityId();
+ }
+
+ }
+
+ /** Task for uninstalling a package. */
+ private final class UninstallPackageTask extends
AbstractPackageInstallTask {
+
+ private PackageId pkgId;
public UninstallPackageTask(final PackageId pkgId, final
TaskResourceGroup erl) {
- super(erl);
+ super("uninstall", 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);
+ public String getSortKey() {
+ return "55-" + getResource().getEntityId();
+ }
- pkg = pkgMgr.open(this.pkgId);
- if ( pkg != null ) {
+ @Override
+ protected void doExecute(InstallationContext ctx, JcrPackageManager
pkgMgr, TaskResource resource)
+ throws RepositoryException, PackageException, IOException {
+ JcrPackage pkg = pkgMgr.open(this.pkgId);
+ try {
+ 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 );
+ 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.
+ /** 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
- */
+ * @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 );
+ 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 if (qualifier != null) {
+ result.append(".0.");
+ cleanupModifier(result, qualifier);
} else {
- result.append( ".0" );
+ result.append(".0");
}
- } else if ( qualifier != null ) {
- result.append( ".0.0." );
- cleanupModifier( result, qualifier );
+ } else if (qualifier != null) {
+ result.append(".0.0.");
+ cleanupModifier(result, qualifier);
} else {
- result.append( ".0.0" );
+ result.append(".0.0");
}
}
} else {
- result.append( "0.0.0." );
- cleanupModifier( result, version );
+ 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 );
+ 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( '_' );
+ result.append('_');
}
}
}
Modified:
sling/trunk/installer/factories/packages/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformerConfiguration.java
URL:
http://svn.apache.org/viewvc/sling/trunk/installer/factories/packages/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformerConfiguration.java?rev=1800415&r1=1800414&r2=1800415&view=diff
==============================================================================
---
sling/trunk/installer/factories/packages/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformerConfiguration.java
(original)
+++
sling/trunk/installer/factories/packages/src/main/java/org/apache/sling/installer/factory/packages/impl/PackageTransformerConfiguration.java
Fri Jun 30 14:16:53 2017
@@ -23,6 +23,8 @@ import org.osgi.service.metatype.annotat
@ObjectClassDefinition(name = "Apache Sling OSGi Installer Package
Transformer", description = "Allows to configure how content packages are
installed through the OSGi installer")
public @interface PackageTransformerConfiguration {
- @AttributeDefinition(name = "Create Snapshots", description = "Boolean
flag indicating whether prior to the installation of the package a snapshot of
the repository should be created.")
+ @AttributeDefinition(name = "Create Snapshots", description = "Boolean
flag indicating whether prior to the installation of the package a snapshot of
the repository should be created. Only relevant when hollow packages are
disabled.")
boolean shouldCreateSnapshots() default true;
+ @AttributeDefinition(name = "Use hollow packages", description = "Boolean
flag indicating whether hollow packages (i.e. packages not containing actual
content) should be used. This prevents the package file from being copied to
the repository first before being installed (i.e. reduces required disk space,
improves speed) but also prevents snapshot creation and uninstallation.")
+ boolean shouldCreateHollowPackages() default false;
}