You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by kw...@apache.org on 2021/05/05 06:38:53 UTC
[jackrabbit-filevault] branch master updated: JCRVLT-517 fix
package cache init in OSGi containers (#136)
This is an automated email from the ASF dual-hosted git repository.
kwin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/jackrabbit-filevault.git
The following commit(s) were added to refs/heads/master by this push:
new 3dba5a2 JCRVLT-517 fix package cache init in OSGi containers (#136)
3dba5a2 is described below
commit 3dba5a20e2aff2aee45ef8fb9c963372bb6d7d98
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Wed May 5 08:38:47 2021 +0200
JCRVLT-517 fix package cache init in OSGi containers (#136)
improve test coverage
fix another issue in FSInstallState not correctly persisting excludes
extract cache into dedicated class
---
vault-core/pom.xml | 28 +-
.../jackrabbit/vault/fs/io/MemoryArchive.java | 7 +-
.../packaging/impl/SubPackageExportProcessor.java | 7 +-
.../vault/packaging/impl/ZipVaultPackage.java | 3 +-
.../vault/packaging/registry/PackageRegistry.java | 8 +-
.../registry/impl/AbstractPackageRegistry.java | 6 +-
.../packaging/registry/impl/FSInstallState.java | 116 ++++--
.../registry/impl/FSInstallStateCache.java | 219 ++++++++++++
.../packaging/registry/impl/FSPackageRegistry.java | 396 ++++++---------------
.../packaging/registry/impl/FSPackageStatus.java | 3 +-
.../registry/impl/FSRegisteredPackage.java | 9 +-
.../registry/impl/JcrPackageRegistry.java | 19 +-
.../jackrabbit/vault/util/InputStreamPump.java | 22 +-
.../packaging/integration/PackageInstallIT.java | 29 +-
.../vault/packaging/integration/SubPackagesIT.java | 2 +-
.../impl}/FSInstallStateTest.java | 39 +-
.../impl}/FSPackageRegistryIT.java | 102 ++++--
.../registry/impl/FSPackageRegistryTest.java | 97 +++++
.../registry/impl/FSRegisteredPackageTest.java | 7 +-
.../packaging/registry/impl/invalid-metadata.xml | 174 +++++++++
.../vault/packaging/registry/impl/test-package.xml | 28 ++
.../test-packages/properties-with-0mtime.zip | Bin 0 -> 453 bytes
22 files changed, 908 insertions(+), 413 deletions(-)
diff --git a/vault-core/pom.xml b/vault-core/pom.xml
index 4466650..2bd9e1d 100644
--- a/vault-core/pom.xml
+++ b/vault-core/pom.xml
@@ -330,6 +330,12 @@
<goal>integration-test</goal>
</goals>
</execution>
+ <execution>
+ <id>verify-its</id>
+ <goals>
+ <goal>verify</goal>
+ </goals>
+ </execution>
</executions>
</plugin>
</plugins>
@@ -351,6 +357,12 @@
<goal>integration-test</goal>
</goals>
</execution>
+ <execution>
+ <id>verify-its</id>
+ <goals>
+ <goal>verify</goal>
+ </goals>
+ </execution>
</executions>
</plugin>
</plugins>
@@ -367,7 +379,7 @@
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
- <id>oak</id>
+ <id>it-with-oak</id>
<goals>
<goal>integration-test</goal>
</goals>
@@ -380,12 +392,24 @@
</configuration>
</execution>
<execution>
- <id>jr</id>
+ <id>it-with-jr</id>
<goals>
<goal>integration-test</goal>
</goals>
<configuration>
<reportNameSuffix>JR</reportNameSuffix>
+ <summaryFile>${project.build.directory}/failsafe-reports/failsafe-summary-jr.xml</summaryFile>
+ </configuration>
+ </execution>
+ <execution>
+ <id>verify-its</id>
+ <goals>
+ <goal>verify</goal>
+ </goals>
+ <configuration>
+ <summaryFiles>
+ <summaryFile>${project.build.directory}/failsafe-reports/failsafe-summary-jr.xml</summaryFile>
+ </summaryFiles>
</configuration>
</execution>
</executions>
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/MemoryArchive.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/MemoryArchive.java
index ad93d9a..7d21d7b 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/MemoryArchive.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/MemoryArchive.java
@@ -28,21 +28,20 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.util.Text;
import org.apache.jackrabbit.vault.fs.api.VaultInputSource;
import org.apache.jackrabbit.vault.fs.config.DefaultMetaInf;
import org.apache.jackrabbit.vault.fs.config.MetaInf;
import org.apache.jackrabbit.vault.fs.config.VaultSettings;
import org.apache.jackrabbit.vault.util.Constants;
import org.apache.jackrabbit.vault.util.InputStreamPump;
-import org.apache.jackrabbit.util.Text;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * Implements a input stream pump that analyzes the stream copies the stream content into memory.
- * The memory archive is initialized via the {@link #run(InputStream)}.
+ * Implements an {@link org.apache.jackrabbit.vault.util.InputStreamPump.Pump} that extracts the relevant parts from the input stream into memory.
+ * The memory archive is initialized via the {@link #run(InputStream)} being called from {@link InputStreamPump}.
*/
public class MemoryArchive extends AbstractArchive implements InputStreamPump.Pump {
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/SubPackageExportProcessor.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/SubPackageExportProcessor.java
index 9a57b73..586c1eb 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/SubPackageExportProcessor.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/SubPackageExportProcessor.java
@@ -38,6 +38,7 @@ import org.apache.jackrabbit.vault.fs.io.AbstractExporter;
import org.apache.jackrabbit.vault.packaging.ExportPostProcessor;
import org.apache.jackrabbit.vault.packaging.JcrPackage;
import org.apache.jackrabbit.vault.packaging.PackageId;
+import org.apache.jackrabbit.vault.packaging.registry.impl.AbstractPackageRegistry;
import org.apache.jackrabbit.util.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -73,7 +74,8 @@ public class SubPackageExportProcessor implements ExportPostProcessor {
if (Text.isDescendantOrEqual(DEFAULT_PACKAGE_ROOT_PATH, nodePath)) {
continue;
}
- String etcPath = DEFAULT_PACKAGE_ROOT_PATH + mgr.getInternalRegistry().getRelativeInstallationPath(pkg.getKey()) + ".zip";
+ mgr.getInternalRegistry();
+ String etcPath = DEFAULT_PACKAGE_ROOT_PATH + "/" + AbstractPackageRegistry.getRelativeInstallationPath(pkg.getKey()) + ".zip";
etcPath = Text.getRelativeParent(etcPath, 1);
// define a workspace filter for the package at the real location
@@ -148,7 +150,8 @@ public class SubPackageExportProcessor implements ExportPostProcessor {
// re-add all the packages in /etc/packages
for (Map.Entry<PackageId, String> pkg : subPackages.entrySet()) {
- String path = DEFAULT_PACKAGE_ROOT_PATH + mgr.getInternalRegistry().getRelativeInstallationPath(pkg.getKey()) + ".zip";
+ mgr.getInternalRegistry();
+ String path = DEFAULT_PACKAGE_ROOT_PATH + "/" + AbstractPackageRegistry.getRelativeInstallationPath(pkg.getKey()) + ".zip";
newFilter.add(new PathFilterSet(path));
}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/ZipVaultPackage.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/ZipVaultPackage.java
index 8ff075a..85d9df8 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/ZipVaultPackage.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/ZipVaultPackage.java
@@ -17,6 +17,7 @@
package org.apache.jackrabbit.vault.packaging.impl;
+import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.util.List;
@@ -48,7 +49,7 @@ import org.slf4j.LoggerFactory;
* Implements a vault package that is a zipped representation of a file vault
* export.
*/
-public class ZipVaultPackage extends PackagePropertiesImpl implements VaultPackage {
+public class ZipVaultPackage extends PackagePropertiesImpl implements VaultPackage, Closeable {
private static final Logger log = LoggerFactory.getLogger(ZipVaultPackage.class);
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageRegistry.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageRegistry.java
index 6f3f480..2a799ef 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageRegistry.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/PackageRegistry.java
@@ -42,14 +42,14 @@ public interface PackageRegistry {
* Checks if this registry contains the package with the given id.
* @param id the package id.
* @return {@code true} if the package is registered.
- * @throws IOException if an I/O error occurrs.
+ * @throws IOException if an I/O error occurs.
*/
boolean contains(@NotNull PackageId id) throws IOException;
/**
* Returns as set of all packages registered in this registry.
* @return a set of package ids.
- * @throws IOException if an I/O error occurrs.
+ * @throws IOException if an I/O error occurs.
*/
@NotNull
Set<PackageId> packages() throws IOException;
@@ -58,7 +58,7 @@ public interface PackageRegistry {
* Opens the package with the given id.
* @param id the package id
* @return the package or {@code null} if it does not exists.
- * @throws IOException if an I/O error occurrs.
+ * @throws IOException if an I/O error occurs.
*/
@Nullable
RegisteredPackage open(@NotNull PackageId id) throws IOException;
@@ -70,7 +70,7 @@ public interface PackageRegistry {
* @param in the input stream to the package data
* @param replace {@code true} if existing package should be replaced.
* @return the new package id.
- * @throws IOException if an I/O error occurrs.
+ * @throws IOException if an I/O error occurs.
* @throws PackageExistsException if the package exists and {@code replace} is {@code false}.
*/
@NotNull
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/AbstractPackageRegistry.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/AbstractPackageRegistry.java
index 6125339..858a9ad 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/AbstractPackageRegistry.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/AbstractPackageRegistry.java
@@ -192,7 +192,7 @@ public abstract class AbstractPackageRegistry implements PackageRegistry, Intern
@NotNull
@Override
public PackageId[] usage(PackageId id) throws IOException {
- TreeSet<PackageId> usages = new TreeSet<PackageId>();
+ TreeSet<PackageId> usages = new TreeSet<>();
for (PackageId pid : packages()) {
try (RegisteredPackage pkg = open(pid)) {
if (pkg == null || !pkg.isInstalled()) {
@@ -227,8 +227,8 @@ public abstract class AbstractPackageRegistry implements PackageRegistry, Intern
* @return the relative path of this package
* @since 2.2
*/
- public String getRelativeInstallationPath(PackageId id) {
- StringBuilder b = new StringBuilder("/");
+ public static String getRelativeInstallationPath(PackageId id) {
+ StringBuilder b = new StringBuilder();
if (id.getGroup().length() > 0) {
b.append(id.getGroup());
b.append("/");
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallState.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallState.java
index 2435beb..1946f3f 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallState.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallState.java
@@ -16,12 +16,13 @@
*/
package org.apache.jackrabbit.vault.packaging.registry.impl;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -34,7 +35,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
-import org.apache.commons.io.FileUtils;
import org.apache.jackrabbit.vault.fs.api.FilterSet.Entry;
import org.apache.jackrabbit.vault.fs.api.PathFilter;
import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
@@ -60,7 +60,7 @@ import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
/**
- * Internal (immutable) State object to cache and pass the relevant metadata around.
+ * Internal (immutable) state object for a package to cache and pass the relevant metadata around.
*/
public class FSInstallState {
@@ -98,9 +98,9 @@ public class FSInstallState {
private static final String TAG_PACKAGEPROPERTIES = "packageproperties";
- private final PackageId packageId;
- private final FSPackageStatus status;
- private Path filePath;
+ private final @NotNull PackageId packageId;
+ private final @NotNull FSPackageStatus status;
+ private final @NotNull Path filePath;
private boolean external;
private Set<Dependency> dependencies = Collections.emptySet();
private Map<PackageId, SubPackageHandling.Option> subPackages = Collections.emptyMap();
@@ -109,14 +109,10 @@ public class FSInstallState {
private WorkspaceFilter filter;
private Properties properties = new Properties();
- public FSInstallState(@NotNull PackageId pid, @NotNull FSPackageStatus status) {
+ public FSInstallState(@NotNull PackageId pid, @NotNull FSPackageStatus status, @NotNull Path filePath) {
this.packageId = pid;
this.status = status;
- }
-
- public FSInstallState withFilePath(Path filePath) {
this.filePath = filePath;
- return this;
}
public FSInstallState withExternal(boolean external) {
@@ -167,12 +163,12 @@ public class FSInstallState {
* @throws IOException in case root tag is correct but structure not parsable as expected
*/
@Nullable
- public static FSInstallState fromFile(File metaFile) throws IOException {
- if (!metaFile.exists()) {
+ public static FSInstallState fromFile(Path metaFile) throws IOException {
+ if (!Files.exists(metaFile)) {
return null;
}
- try (InputStream in = FileUtils.openInputStream(metaFile)) {
- return fromStream(in, metaFile.getPath());
+ try (InputStream in = Files.newInputStream(metaFile)) {
+ return fromStream(in, metaFile.toString());
}
}
@@ -232,8 +228,7 @@ public class FSInstallState {
}
}
- return new FSInstallState(PackageId.fromString(packageId), status)
- .withFilePath(filePath)
+ return new FSInstallState(PackageId.fromString(packageId), status, filePath)
.withExternal(external)
.withSize(size)
.withFilter(filter)
@@ -287,7 +282,7 @@ public class FSInstallState {
DefaultPathFilter pf = new DefaultPathFilter(((Element) rule).getAttribute(ATTR_INCLUDE));
pfs.addInclude(pf);
} else if (((Element) rule).hasAttribute(ATTR_EXCLUDE)) {
- DefaultPathFilter pf = new DefaultPathFilter(((Element) rule).getAttribute(ATTR_INCLUDE));
+ DefaultPathFilter pf = new DefaultPathFilter(((Element) rule).getAttribute(ATTR_EXCLUDE));
pfs.addExclude(pf);
}
}
@@ -305,8 +300,9 @@ public class FSInstallState {
* @param file The files to save the state to
* @throws IOException if an error occurs.
*/
- public void save(File file) throws IOException {
- try (OutputStream out = FileUtils.openOutputStream(file)) {
+ public void save(Path file) throws IOException {
+ Files.createDirectories(file.getParent());
+ try (OutputStream out = Files.newOutputStream(file, StandardOpenOption.CREATE)) {
save(out);
}
}
@@ -417,4 +413,84 @@ public class FSInstallState {
public Properties getProperties() {
return properties;
}
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((dependencies == null) ? 0 : dependencies.hashCode());
+ result = prime * result + (external ? 1231 : 1237);
+ result = prime * result + ((filePath == null) ? 0 : filePath.hashCode());
+ result = prime * result + ((filter == null) ? 0 : filter.hashCode());
+ result = prime * result + ((installTime == null) ? 0 : installTime.hashCode());
+ result = prime * result + ((packageId == null) ? 0 : packageId.hashCode());
+ result = prime * result + ((properties == null) ? 0 : properties.hashCode());
+ result = prime * result + (int) (size ^ (size >>> 32));
+ result = prime * result + ((status == null) ? 0 : status.hashCode());
+ result = prime * result + ((subPackages == null) ? 0 : subPackages.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (getClass() != obj.getClass())
+ return false;
+ FSInstallState other = (FSInstallState) obj;
+ if (dependencies == null) {
+ if (other.dependencies != null)
+ return false;
+ } else if (!dependencies.equals(other.dependencies))
+ return false;
+ if (external != other.external)
+ return false;
+ if (filePath == null) {
+ if (other.filePath != null)
+ return false;
+ } else if (!filePath.equals(other.filePath))
+ return false;
+ if (filter == null) {
+ if (other.filter != null)
+ return false;
+ } else if (!filter.equals(other.filter))
+ return false;
+ if (installTime == null) {
+ if (other.installTime != null)
+ return false;
+ } else if (!installTime.equals(other.installTime))
+ return false;
+ if (packageId == null) {
+ if (other.packageId != null)
+ return false;
+ } else if (!packageId.equals(other.packageId))
+ return false;
+ if (properties == null) {
+ if (other.properties != null)
+ return false;
+ } else if (!properties.equals(other.properties))
+ return false;
+ if (size != other.size)
+ return false;
+ if (status != other.status)
+ return false;
+ if (subPackages == null) {
+ if (other.subPackages != null)
+ return false;
+ } else if (!subPackages.equals(other.subPackages))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "FSInstallState [" + (packageId != null ? "packageId=" + packageId + ", " : "")
+ + (status != null ? "status=" + status + ", " : "") + (filePath != null ? "filePath=" + filePath + ", " : "") + "external="
+ + external + ", " + (dependencies != null ? "dependencies=" + dependencies + ", " : "")
+ + (subPackages != null ? "subPackages=" + subPackages + ", " : "")
+ + (installTime != null ? "installTime=" + installTime + ", " : "") + "size=" + size + ", "
+ + (filter != null ? "filter=" + filter + ", " : "") + (properties != null ? "properties=" + properties : "") + "]";
+ }
}
\ No newline at end of file
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallStateCache.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallStateCache.java
new file mode 100644
index 0000000..fb019d2
--- /dev/null
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallStateCache.java
@@ -0,0 +1,219 @@
+/*
+ * 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.jackrabbit.vault.packaging.registry.impl;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.AbstractMap;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Stream;
+
+import org.apache.jackrabbit.vault.packaging.PackageId;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Persisted cache of all {@link FSInstallState} objects for all packages in a registry.
+ * Populated on demand and written back immediately for every modifying operation.
+ * Is thread-safe.
+ */
+class FSInstallStateCache extends AbstractMap<PackageId, FSInstallState> {
+
+ /** Wraps a checked IOExceptioin in a unchecked exception, this is potentially thrown from all Map operations */
+ final class UncheckedIOException extends RuntimeException {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1188317232809121358L;
+ private final IOException ioException;
+
+ public UncheckedIOException(IOException ioException) {
+ super(ioException.getMessage(), ioException);
+ this.ioException = ioException;
+ }
+
+ public IOException getIOException() {
+ return ioException;
+ }
+ }
+
+ /**
+ * Extension for metadata files
+ */
+ private static final String META_EXTENSION = ".xml";
+
+ private final Map<PackageId, FSInstallState> cache = new ConcurrentHashMap<>();
+ private boolean isInitialized = false;
+
+ /**
+ * Contains a map of all filesystem paths to package IDs
+ */
+ private Map<Path, PackageId> pathIdMapping = new ConcurrentHashMap<>();
+
+ private final Path homeDir;
+
+ public FSInstallStateCache(Path homeDir) throws IOException {
+ this.homeDir = homeDir;
+ Files.createDirectories(homeDir);
+ }
+
+ /**
+ * Loads all state from files persisted in configured homeDir, adds to cache and returns all cached {@code PackageId}s.
+ * @throws IOException
+ */
+ private synchronized void load() throws IOException {
+ Map<PackageId, FSInstallState> cacheEntries = new HashMap<>();
+ Map<Path, PackageId> idMapping = new HashMap<>();
+
+ // recursively find meta file
+ try (Stream<Path> stream = Files.walk(homeDir, 10)) {
+ stream.filter(Files::isRegularFile).filter(p -> p.toString().endsWith(META_EXTENSION)).forEach(
+ p -> {
+ FSInstallState state;
+ try {
+ state = FSInstallState.fromFile(p);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ if (state != null) {
+ PackageId id = state.getPackageId();
+ if (id != null) {
+ cacheEntries.put(id, state);
+ idMapping.put(state.getFilePath(), id);
+ }
+ }
+ }
+ );
+ }
+
+ cache.putAll(cacheEntries);
+ pathIdMapping.putAll(idMapping);
+ isInitialized = true;
+ }
+
+ @Override
+ public Set<Entry<PackageId, FSInstallState>> entrySet() {
+ if (!isInitialized) {
+ try {
+ load();
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ return cache.entrySet();
+ }
+
+ /**
+ * Returns the meta data file of the package with the given Id.
+ *
+ * @param id The package Id.
+ * @return the meta data file.
+ */
+ @NotNull
+ private Path getPackageMetaDataFile(@NotNull PackageId id) {
+ final String path = AbstractPackageRegistry.getRelativeInstallationPath(id);
+ return homeDir.resolve(path + ".xml");
+ }
+
+ @NotNull
+ public Path getPackageFile(@NotNull PackageId id) {
+ String path = AbstractPackageRegistry.getRelativeInstallationPath(id);
+ return homeDir.resolve(path + ".zip");
+ }
+
+
+ /**
+ * Shortcut to just change the status of a package - implicitly sets the installtime when switching to EXTRACTED
+ *
+ * @param pid PackageId of the package to update
+ * @param targetStatus Status to update
+ * @throws IOException If an I/O error occurs.
+ */
+ public void updatePackageStatus(PackageId pid, FSPackageStatus targetStatus) throws IOException {
+ FSInstallState state = get(pid);
+ if (state == null) {
+ throw new IllegalArgumentException("No package with pid " + pid + " registered");
+ }
+ Long installTime = state.getInstallationTime();
+ if (FSPackageStatus.EXTRACTED == targetStatus) {
+ installTime = Calendar.getInstance().getTimeInMillis();
+ }
+ FSInstallState targetState = new FSInstallState(pid, targetStatus, state.getFilePath())
+ .withDependencies(state.getDependencies())
+ .withSubPackages(state.getSubPackages())
+ .withInstallTime(installTime)
+ .withSize(state.getSize())
+ .withProperties(state.getProperties())
+ .withExternal(state.isExternal());
+ put(pid, targetState);
+ }
+
+ @Override
+ public FSInstallState get(Object key) {
+ FSInstallState state = super.get(key);
+ if (state == null) {
+ PackageId pid = (PackageId) key;
+ // fallback (only for get(..), but does not affect size(), entrySet(), hasKey(), keys(), values()), detects changes on the filesystem done outside this class
+ Path metaFile = getPackageMetaDataFile(pid);
+ try {
+ state = FSInstallState.fromFile(metaFile);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ if (state != null) {
+ cache.put(pid, state);
+ pathIdMapping.put(state.getFilePath(), pid);
+ }
+ }
+ return state;
+ }
+
+ @Override
+ public FSInstallState put(PackageId key, FSInstallState value) {
+ FSInstallState state = cache.put(key, value);
+ // persist changes
+ try {
+ value.save(getPackageMetaDataFile(key));
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ return state;
+ }
+
+ @Override
+ public FSInstallState remove(Object key) {
+ FSInstallState state = super.remove(key);
+ if (state != null) {
+ PackageId pid = (PackageId) key;
+ Path metaData = getPackageMetaDataFile(pid);
+ try {
+ Files.delete(metaData);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ return state;
+ }
+
+ public PackageId getIdForFile(Path file) {
+ return pathIdMapping.get(file);
+ }
+}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageRegistry.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageRegistry.java
index f8c19c6..b2c793d 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageRegistry.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageRegistry.java
@@ -19,9 +19,10 @@ package org.apache.jackrabbit.vault.packaging.registry.impl;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.file.Files;
import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -29,12 +30,11 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
-import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.util.Text;
import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
@@ -60,7 +60,6 @@ import org.apache.jackrabbit.vault.packaging.registry.PackageRegistry;
import org.apache.jackrabbit.vault.packaging.registry.RegisteredPackage;
import org.apache.jackrabbit.vault.util.InputStreamPump;
import org.apache.jackrabbit.vault.util.PlatformNameFormat;
-import org.apache.jackrabbit.util.Text;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.osgi.framework.BundleContext;
@@ -87,39 +86,21 @@ import org.slf4j.LoggerFactory;
@Designate(ocd = FSPackageRegistry.Config.class)
public class FSPackageRegistry extends AbstractPackageRegistry {
- private static final String REPOSITORY_HOME = "repository.home";
+ protected static final String REPOSITORY_HOME = "repository.home";
/**
* default logger
*/
private static final Logger log = LoggerFactory.getLogger(FSPackageRegistry.class);
- /**
- * Suffixes for metadata files
- */
- private final String[] META_SUFFIXES = {"xml"};
-
- private Map<PackageId, FSInstallState> stateCache = new ConcurrentHashMap<>();
-
- /**
- * Contains a map of all filesystem paths to package IDs
- */
- private Map<Path, PackageId> pathIdMapping = new ConcurrentHashMap<>();
-
+ private FSInstallStateCache stateCache;
- private boolean packagesInitializied = false;
@Reference
private PackageEventDispatcher dispatcher;
- private File homeDir;
-
private InstallationScope scope = InstallationScope.UNSCOPED;
- private File getHomeDir() {
- return homeDir;
- }
-
/**
* Creates a new FSPackageRegistry based on the given home directory.
*
@@ -160,10 +141,9 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
public FSPackageRegistry(@NotNull File homeDir, InstallationScope scope, @Nullable AbstractPackageRegistry.SecurityConfig securityConfig, boolean isStrict) throws IOException {
super(securityConfig, isStrict);
- this.homeDir = homeDir;
- log.info("Jackrabbit Filevault FS Package Registry initialized with home location {}", this.homeDir.getPath());
+ log.info("Jackrabbit Filevault FS Package Registry initialized with home location {}", homeDir.getPath());
this.scope = scope;
- loadPackageCache();
+ this.stateCache = new FSInstallStateCache(homeDir.toPath());
}
/**
@@ -175,16 +155,17 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
}
@Activate
- public void activate(BundleContext context, Config config) {
- this.homeDir = context.getProperty(REPOSITORY_HOME) != null ? (
+ public void activate(BundleContext context, Config config) throws IOException {
+ File homeDir = context.getProperty(REPOSITORY_HOME) != null ? (
new File(config.homePath()).isAbsolute() ? new File(config.homePath()) : new File(context.getProperty(REPOSITORY_HOME) + "/" + config.homePath())) :
context.getDataFile(config.homePath());
if (!homeDir.exists()) {
homeDir.mkdirs();
}
- log.info("Jackrabbit Filevault FS Package Registry initialized with home location {}", this.homeDir.getPath());
+ log.info("Jackrabbit Filevault FS Package Registry initialized with home location {}", homeDir.getPath());
this.scope = InstallationScope.valueOf(config.scope());
this.securityConfig = new AbstractPackageRegistry.SecurityConfig(config.authIdsForHookExecution(), config.authIdsForRootInstallation());
+ this.stateCache = new FSInstallStateCache(homeDir.toPath());
}
@ObjectClassDefinition(
@@ -199,7 +180,7 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
description = "Allows to limit the installation scope of this Apache Jackrabbit FS Package Registry Service. "
+ "Packages installed from this registry may be unscoped (unfiltered), "
+ "application scoped (only content for /apps & /libs) "
- + "or content scoped (all content despite of /libs & /apps)",
+ + "or content scoped (all content except for /libs & /apps)",
options = {
@Option(label = "Unscoped", value = "UNSCOPED"),
@Option(label = "Application Scoped", value = "APPLICATION_SCOPED"),
@@ -241,68 +222,57 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
@Override
public RegisteredPackage open(@NotNull PackageId id) throws IOException {
FSInstallState state = getInstallState(id);
- return FSPackageStatus.NOTREGISTERED != state.getStatus() ? new FSRegisteredPackage(this, state) : null;
+ return state != null ? new FSRegisteredPackage(this, state) : null;
}
@Override
public boolean contains(@NotNull PackageId id) throws IOException {
- return stateCache.containsKey(id);
+ return getInstallState(id) != null; // don't use hasKey as otherwise there is no fallback for lazily loading metadata files
}
- @Nullable
- private File getPackageFile(@NotNull PackageId id) {
+ @Nullable
+ FSInstallState getInstallState(@NotNull PackageId id) throws IOException {
try {
- FSInstallState state = getInstallState(id);
- if (FSPackageStatus.NOTREGISTERED == state.getStatus()) {
- return buildPackageFile(id);
- } else {
- return state.getFilePath().toFile();
- }
- } catch (IOException e) {
- log.error("Couldn't get install state of packageId {}", id, e);
+ return stateCache.get(id);
+ } catch (FSInstallStateCache.UncheckedIOException e) {
+ throw e.getIOException();
}
- return null;
- }
-
- private File buildPackageFile(@NotNull PackageId id) {
- String path = getInstallationPath(id);
- return new File(getHomeDir(), path + ".zip");
}
/**
- * Returns the meta data file of the package with the given Id.
- *
- * @param id The package Id.
- * @return the meta data file.
+ *
+ * @param id
+ * @return the file pointing to an existing or new package with the given id
+ * @throws IOException
*/
@NotNull
- private File getPackageMetaDataFile(@NotNull PackageId id) {
- final String path = getInstallationPath(id);
- return new File(getHomeDir(), path + ".xml");
+ private Path getPackageFile(@NotNull PackageId id) throws IOException {
+ FSInstallState state = getInstallState(id);
+ if (state == null) {
+ return stateCache.getPackageFile(id);
+ } else {
+ return state.getFilePath();
+ }
}
/**
* Opens the package of a file with the given Id.
* @param id The Id of package file.
* @return the package
- * @throws IOException if an I/O error occurrs.
+ * @throws IOException if an I/O error occurs.
*/
@NotNull
- protected VaultPackage openPackageFile(@NotNull PackageId id) throws IOException {
- File pkg = getPackageFile(id);
- if (pkg == null) {
- throw new IOException("Could not find package file for id " + id);
- }
+ protected VaultPackage openPackageFile(@NotNull PackageId id) throws IOException, NoSuchPackageException {
+ Path pkg = getPackageFile(id);
- if (pkg.exists() && pkg.length() > 0) {
- try {
- return new ZipVaultPackage(pkg, false, true);
- } catch (IOException e) {
- log.error("Cloud not open file {} as ZipVaultPackage.", pkg.getPath(), e);
- throw e;
- }
+ if (Files.exists(pkg) && Files.size(pkg) > 0) {
+ return new ZipVaultPackage(pkg.toFile(), false, true);
} else {
- return new HollowVaultPackage(getInstallState(id).getProperties());
+ FSInstallState state = getInstallState(id);
+ if (state == null) {
+ throw new NoSuchPackageException().setId(id);
+ }
+ return new HollowVaultPackage(state.getProperties());
}
}
@@ -314,8 +284,8 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
public DependencyReport analyzeDependencies(@NotNull PackageId id, boolean onlyInstalled) throws IOException, NoSuchPackageException {
List<Dependency> unresolved = new LinkedList<>();
List<PackageId> resolved = new LinkedList<>();
- FSInstallState state = getInstallState(id);
- if (FSPackageStatus.NOTREGISTERED == state.getStatus()) {
+ FSInstallState state = stateCache.get(id);
+ if (state == null) {
throw new NoSuchPackageException().setId(id);
}
@@ -369,8 +339,11 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
* @throws IOException If an I/O error occurs.
*/
boolean isInstalled(PackageId id) throws IOException {
- FSPackageStatus status = getInstallState(id).getStatus();
- return FSPackageStatus.EXTRACTED == status;
+ FSInstallState state = getInstallState(id);
+ if (state != null) {
+ return FSPackageStatus.EXTRACTED == state.getStatus();
+ }
+ return false;
}
/**
@@ -381,30 +354,26 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
public PackageId register(@NotNull InputStream in, boolean replace) throws IOException, PackageExistsException {
return register(in, replace, null);
}
-
- /**
- * {@inheritDoc}
- */
+
@NotNull
private PackageId register(@NotNull InputStream in, boolean replace, Dependency autoDependency) throws IOException, PackageExistsException {
ZipVaultPackage pkg = upload(in, replace);
Map<PackageId, SubPackageHandling.Option> subpackages = registerSubPackages(pkg, replace);
- File pkgFile = buildPackageFile(pkg.getId());
+ Path pkgFile = getPackageFile(pkg.getId());
HashSet<Dependency> dependencies = new HashSet<>();
dependencies.addAll(Arrays.asList(pkg.getDependencies()));
if (autoDependency != null) {
dependencies.add(autoDependency);
}
- FSInstallState state = new FSInstallState(pkg.getId(), FSPackageStatus.REGISTERED)
- .withFilePath(pkgFile.toPath())
+ FSInstallState state = new FSInstallState(pkg.getId(), FSPackageStatus.REGISTERED, pkgFile)
.withDependencies(dependencies)
.withSubPackages(subpackages)
.withFilter(pkg.getArchive().getMetaInf().getFilter())
.withSize(pkg.getSize())
.withProperties(pkg.getArchive().getMetaInf().getProperties())
.withExternal(false);
- setInstallState(state);
+ stateCache.put(pkg.getId(), state);
return pkg.getId();
}
@@ -480,63 +449,57 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
}
}
- /**
- * {@inheritDoc}
- */
- public ZipVaultPackage upload(InputStream in, boolean replace)
+ protected ZipVaultPackage upload(InputStream in, boolean replace)
throws IOException, PackageExistsException {
+ Path tempFile = Files.createTempFile("upload", ".zip");
MemoryArchive archive = new MemoryArchive(false);
- File tempFile = File.createTempFile("upload", ".zip");
try (InputStreamPump pump = new InputStreamPump(in, archive)) {
// this will cause the input stream to be consumed and the memory
// archive being initialized.
try {
-
- FileUtils.copyInputStreamToFile(pump, tempFile);
- } catch (Exception e) {
- String msg = "Stream could be read successfully.";
- log.error(msg);
+ Files.copy(pump, tempFile, StandardCopyOption.REPLACE_EXISTING);
+ } catch (IOException e) {
+ String msg = "Stream could not be read successfully.";
throw new IOException(msg, e);
}
+ }
+ if (archive.getJcrRoot() == null) {
+ String msg = "Stream is not a content package. Missing 'jcr_root'.";
+ throw new IOException(msg);
+ }
- if (archive.getJcrRoot() == null) {
- String msg = "Stream is not a content package. Missing 'jcr_root'.";
- log.error(msg);
- throw new IOException(msg);
- }
-
- final MetaInf inf = archive.getMetaInf();
- PackageId pid = inf.getPackageProperties().getId();
+ final MetaInf inf = archive.getMetaInf();
+ PackageId pid = inf.getPackageProperties().getId();
- // invalidate pid if path is unknown
- if (pid == null) {
- throw new IllegalArgumentException("Unable to create package. No package pid set.");
- }
- if (!pid.isValid()) {
- throw new IllegalArgumentException("Unable to create package. Illegal package name.");
- }
+ // invalidate pid if path is unknown
+ if (pid == null) {
+ throw new IllegalArgumentException("Unable to create package. No package pid set.");
+ }
+ if (!pid.isValid()) {
+ throw new IllegalArgumentException("Unable to create package. Illegal package name.");
+ }
- File oldPkgFile = getPackageFile(pid);
- FSInstallState state = getInstallState(pid);
+ Path pkgFile = getPackageFile(pid);
+ FSInstallState state = getInstallState(pid);
- if (oldPkgFile != null && oldPkgFile.exists()) {
- if (replace && !state.isExternal()) {
- oldPkgFile.delete();
- } else {
- throw new PackageExistsException("Package already exists: " + pid).setId(pid);
- }
+ if (Files.exists(pkgFile)) {
+ if (replace && !state.isExternal()) {
+ Files.delete(pkgFile);
+ } else {
+ throw new PackageExistsException("Package already exists: " + pid).setId(pid);
}
-
- ZipVaultPackage pkg = new ZipVaultPackage(archive, true);
- registerSubPackages(pkg, replace);
- File pkgFile = buildPackageFile(pid);
- FileUtils.moveFile(tempFile, pkgFile);
- dispatch(Type.UPLOAD, pid, null);
- return pkg;
+ } else {
+ Files.createDirectories(pkgFile.getParent());
}
+ ZipVaultPackage pkg = new ZipVaultPackage(archive, true);
+ registerSubPackages(pkg, replace);
+ Files.move(tempFile, pkgFile);
+ dispatch(Type.UPLOAD, pid, null);
+ return pkg;
+
}
/**
@@ -545,69 +508,55 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
@NotNull
@Override
public PackageId register(@NotNull File file, boolean replace) throws IOException, PackageExistsException {
- try (ZipVaultPackage pack = new ZipVaultPackage(file, false, true)) {
- File pkgFile = buildPackageFile(pack.getId());
- if (pkgFile.exists()) {
- if (replace) {
- pkgFile.delete();
- } else {
- throw new PackageExistsException("Package already exists: " + pack.getId()).setId(pack.getId());
- }
- }
- Map<PackageId, SubPackageHandling.Option> subpackages = registerSubPackages(pack, replace);
- FileUtils.copyFile(file, pkgFile);
- Set<Dependency> dependencies = new HashSet<>(Arrays.asList(pack.getDependencies()));
- FSInstallState state = new FSInstallState(pack.getId(), FSPackageStatus.REGISTERED)
- .withFilePath(pkgFile.toPath())
- .withDependencies(dependencies)
- .withSubPackages(subpackages)
- .withFilter(pack.getArchive().getMetaInf().getFilter())
- .withSize(pack.getSize())
- .withProperties(pack.getArchive().getMetaInf().getProperties())
- .withExternal(false);
- setInstallState(state);
- return pack.getId();
- }
+ return doRegister(file, replace, false);
}
@NotNull
@Override
public PackageId registerExternal(@NotNull File file, boolean replace) throws IOException, PackageExistsException {
- if (!replace && pathIdMapping.containsKey(file.toPath())) {
- PackageId pid = pathIdMapping.get(file.toPath());
- throw new PackageExistsException("Package already exists: " + pid).setId(pid);
- }
- ZipVaultPackage pack = new ZipVaultPackage(file, false, true);
- try {
+ return doRegister(file, replace, true);
+ }
+ @NotNull
+ private PackageId doRegister(@NotNull File file, boolean replace, boolean external) throws IOException, PackageExistsException {
+ // detect collisions without parsing the package to speed things up
+ PackageId oldPackageId = stateCache.getIdForFile(file.toPath());
+ if (!replace && oldPackageId != null) {
+ throw new PackageExistsException("Package already exists: " + oldPackageId).setId(oldPackageId);
+ }
+ try (ZipVaultPackage pack = new ZipVaultPackage(file, false, true)) {
FSInstallState state = getInstallState(pack.getId());
- if (!(FSPackageStatus.NOTREGISTERED == state.getStatus())) {
+ if (state != null) {
if (replace) {
try {
remove(pack.getId());
} catch (NoSuchPackageException e) {
- log.error("Status isn't NOTREGISTERD but no metafile exists to remove", e);
+ log.error("No metafile exists to remove", e);
}
} else {
throw new PackageExistsException("Package already exists: " + pack.getId()).setId(pack.getId());
}
}
+ final Path newPackageFile;
+ if (!external) {
+ // copy to registry path
+ newPackageFile = getPackageFile(pack.getId());
+ Files.createDirectories(newPackageFile.getParent());
+ Files.copy(file.toPath(), newPackageFile);
+ } else {
+ newPackageFile = file.toPath();
+ }
Map<PackageId, SubPackageHandling.Option> subpackages = registerSubPackages(pack, replace);
Set<Dependency> dependencies = new HashSet<>(Arrays.asList(pack.getDependencies()));
- FSInstallState targetState = new FSInstallState(pack.getId(), FSPackageStatus.REGISTERED)
- .withFilePath(file.toPath())
+ FSInstallState targetState = new FSInstallState(pack.getId(), FSPackageStatus.REGISTERED, newPackageFile)
.withDependencies(dependencies)
.withSubPackages(subpackages)
.withFilter(pack.getArchive().getMetaInf().getFilter())
.withSize(pack.getSize())
.withProperties(pack.getArchive().getMetaInf().getProperties())
- .withExternal(true);
- setInstallState(targetState);
+ .withExternal(external);
+ stateCache.put(pack.getId(), targetState);
return pack.getId();
- } finally {
- if (!pack.isClosed()) {
- pack.close();
- }
}
}
@@ -616,18 +565,13 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
*/
@Override
public void remove(@NotNull PackageId id) throws IOException, NoSuchPackageException {
- FSInstallState state = getInstallState(id);
- File metaData = getPackageMetaDataFile(id);
-
- if (!metaData.exists()) {
+ FSInstallState state = stateCache.remove(id);
+ if (state == null) {
throw new NoSuchPackageException().setId(id);
}
- metaData.delete();
-
if (!state.isExternal()) {
- getPackageFile(id).delete();
+ Files.delete(state.getFilePath());
}
- updateInstallState(id, FSPackageStatus.NOTREGISTERED);
dispatch(PackageEvent.Type.REMOVE, id, null);
}
@@ -637,48 +581,7 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
@NotNull
@Override
public Set<PackageId> packages() throws IOException {
- return packagesInitializied ? stateCache.keySet() : loadPackageCache();
- }
-
- /**
- * Loads all state from files persisted in configured homeDir, adds to cache and returns all cached {@code PackageId}s.
- *
- * @return {@code Set} of all cached {@code PackageId}s
- *
- * @throws IOException If an I/O error occurs
- */
- private Set<PackageId> loadPackageCache() throws IOException {
- Map<PackageId, FSInstallState> cacheEntries = new HashMap<>();
- Map<Path, PackageId> idMapping = new HashMap<>();
-
-
- Collection<File> files = FileUtils.listFiles(getHomeDir(), META_SUFFIXES, true);
- for (File file : files) {
- FSInstallState state = FSInstallState.fromFile(file);
- if (state != null) {
- PackageId id = state.getPackageId();
- if (id != null) {
- cacheEntries.put(id, state);
- idMapping.put(state.getFilePath(), id);
-
- }
- }
- }
- stateCache.putAll(cacheEntries);
- pathIdMapping.putAll(idMapping);
- packagesInitializied = true;
- return cacheEntries.keySet();
- }
-
- /**
- * Returns the path of this package.this also includes the version, but
- * never the extension (.zip).
- *
- * @param id the package id
- * @return the path of this package
- */
- public String getInstallationPath(PackageId id) {
- return getRelativeInstallationPath(id);
+ return stateCache.keySet();
}
/**
@@ -721,7 +624,7 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
}
((ZipVaultPackage)vltPkg).extract(session, opts, getSecurityConfig(), isStrictByDefault());
dispatch(PackageEvent.Type.EXTRACT, pkg.getId(), null);
- updateInstallState(vltPkg.getId(), FSPackageStatus.EXTRACTED);
+ stateCache.updatePackageStatus(vltPkg.getId(), FSPackageStatus.EXTRACTED);
} catch (RepositoryException e) {
throw new IOException(e);
@@ -738,73 +641,4 @@ public class FSPackageRegistry extends AbstractPackageRegistry {
throw new PackageException(msg);
}
- /**
- * Shortcut to just change the status of a package - implicitly sets the installtime when switching to EXTRACTED
- *
- * @param pid PackageId of the package to update
- * @param targetStatus Status to update
- * @throws IOException If an I/O error occurs.
- */
- private void updateInstallState(PackageId pid, FSPackageStatus targetStatus) throws IOException {
- FSInstallState state = getInstallState(pid);
- Long installTime = state.getInstallationTime();
- if (FSPackageStatus.EXTRACTED == targetStatus) {
- installTime = Calendar.getInstance().getTimeInMillis();
- }
- FSInstallState targetState = new FSInstallState(pid, targetStatus)
- .withFilePath(state.getFilePath())
- .withDependencies(state.getDependencies())
- .withSubPackages(state.getSubPackages())
- .withInstallTime(installTime)
- .withSize(state.getSize())
- .withProperties(state.getProperties())
- .withExternal(state.isExternal());
- setInstallState(targetState);
- }
-
- /**
- * Persists the installState to a metadatafile and adds current state to cache
- * @param state
- * @throws IOException
- */
- private void setInstallState(@NotNull FSInstallState state) throws IOException {
- PackageId pid = state.getPackageId();
- File metaData = getPackageMetaDataFile(pid);
-
- if (state.getStatus() == FSPackageStatus.NOTREGISTERED) {
- pathIdMapping.remove(stateCache.get(pid).getFilePath());
- metaData.delete();
- stateCache.remove(pid);
- } else {
- state.save(metaData);
- stateCache.put(pid, state);
- pathIdMapping.put(state.getFilePath(), pid);
- }
- }
-
- /**
- * Retrieves {@code InstallState} from cache, falls back to reading from metafile and returns state for {@code FSPackageStatus.NOTREGISTERED} in case not found.
- *
- * @param pid the PackageId of the package to retrieve the install state from.
- * @return {@code InstallState} found for given {@code PackageId} or a fresh one with status {@code FSPackageStatus.NOTREGISTERED}
- *
- * @throws IOException if an I/O error occurs.
- */
- @NotNull
- public FSInstallState getInstallState(PackageId pid) throws IOException {
- if (stateCache.containsKey(pid)) {
- return stateCache.get(pid);
- } else {
- File metaFile = getPackageMetaDataFile(pid);
- FSInstallState state = FSInstallState.fromFile(metaFile);
- if (state != null) {
- //theoretical file - should only be feasible when manipulating on filesystem, writing metafile automatically updates cache
- stateCache.put(pid, state);
- pathIdMapping.put(state.getFilePath(), pid);
- }
- return state != null ? state : new FSInstallState(pid, FSPackageStatus.NOTREGISTERED);
- }
- }
-
-
}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageStatus.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageStatus.java
index d340228..9bc02e3 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageStatus.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageStatus.java
@@ -22,7 +22,6 @@ package org.apache.jackrabbit.vault.packaging.registry.impl;
public enum FSPackageStatus {
REGISTERED,
- EXTRACTED,
- NOTREGISTERED
+ EXTRACTED
}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSRegisteredPackage.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSRegisteredPackage.java
index c7fa409..f22bbdf 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSRegisteredPackage.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSRegisteredPackage.java
@@ -21,6 +21,7 @@ import java.util.Calendar;
import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
import org.apache.jackrabbit.vault.packaging.Dependency;
+import org.apache.jackrabbit.vault.packaging.NoSuchPackageException;
import org.apache.jackrabbit.vault.packaging.PackageId;
import org.apache.jackrabbit.vault.packaging.PackageProperties;
import org.apache.jackrabbit.vault.packaging.VaultPackage;
@@ -38,7 +39,7 @@ public class FSRegisteredPackage implements RegisteredPackage {
/**
* default logger
*/
- private static final Logger log = LoggerFactory.getLogger(FSPackageRegistry.class);
+ private static final Logger log = LoggerFactory.getLogger(FSRegisteredPackage.class);
private FSPackageRegistry registry;
@@ -69,7 +70,11 @@ public class FSRegisteredPackage implements RegisteredPackage {
@Override
public VaultPackage getPackage() throws IOException {
if (this.vltPkg == null) {
- this.vltPkg = registry.openPackageFile(getId());
+ try {
+ this.vltPkg = registry.openPackageFile(getId());
+ } catch (NoSuchPackageException e) {
+ throw new IOException("Registry does not/no longer know package with id " + getId(), e);
+ }
}
return this.vltPkg;
}
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/JcrPackageRegistry.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/JcrPackageRegistry.java
index cfc1419..0c5ac0f 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/JcrPackageRegistry.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/registry/impl/JcrPackageRegistry.java
@@ -272,7 +272,7 @@ public class JcrPackageRegistry extends AbstractPackageRegistry {
private Node getPackageNode(@NotNull PackageId id) throws RepositoryException {
String relPath = getRelativeInstallationPath(id);
for (String pfx: packRootPaths) {
- String path = pfx + relPath;
+ String path = pfx + "/" + relPath;
String[] exts = new String[]{"", ".zip", ".jar"};
for (String ext: exts) {
if (session.nodeExists(path + ext)) {
@@ -357,21 +357,14 @@ public class JcrPackageRegistry extends AbstractPackageRegistry {
throws RepositoryException, IOException, PackageExistsException {
MemoryArchive archive = new MemoryArchive(true);
- InputStreamPump pump = new InputStreamPump(in , archive);
-
- // this will cause the input stream to be consumed and the memory archive being initialized.
- Binary bin = session.getValueFactory().createBinary(pump);
- if (pump.getError() != null) {
- Exception error = pump.getError();
- log.error("Error while reading from input stream.", error);
- bin.dispose();
- archive.close();
- throw new IOException("Error while reading from input stream", error);
+ Binary bin;
+ try (InputStreamPump pump = new InputStreamPump(in , archive)) {
+ // this will cause the input stream to be consumed and the memory archive being initialized.
+ bin = session.getValueFactory().createBinary(pump);
}
if (archive.getJcrRoot() == null) {
String msg = "Stream is not a content package. Missing 'jcr_root'.";
- log.error(msg);
bin.dispose();
archive.close();
throw new IOException(msg);
@@ -771,7 +764,7 @@ public class JcrPackageRegistry extends AbstractPackageRegistry {
* @since 2.2
*/
public String getInstallationPath(PackageId id) {
- return packRootPaths[0] + getRelativeInstallationPath(id);
+ return packRootPaths[0] + "/" + getRelativeInstallationPath(id);
}
@Override
diff --git a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/InputStreamPump.java b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/InputStreamPump.java
index 63027ee..b776692 100644
--- a/vault-core/src/main/java/org/apache/jackrabbit/vault/util/InputStreamPump.java
+++ b/vault-core/src/main/java/org/apache/jackrabbit/vault/util/InputStreamPump.java
@@ -22,11 +22,19 @@ import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import org.apache.commons.io.input.CloseShieldInputStream;
+import org.apache.commons.io.input.TeeInputStream;
+import org.apache.jackrabbit.vault.fs.io.MemoryArchive;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * {@code InputStreamPump}...
+ * An input stream pump feeds a {@link InputStreamPump.Pump} in a dedicated thread with the input read from
+ * the given input stream.
+ * This is similar to a {@link TeeInputStream} but leverages {@link PipedInputStream} and {@link PipedOutputStream}
+ * and can execute additional tasks in the additional thread consuming the PipedInputStream.
+ * Only after calling {@link #close()} the PipedInputStream has been fully consumed (as it waits for the pump's thread to complete).
+ *
+ * @see MemoryArchive
*/
public class InputStreamPump extends InputStream {
@@ -43,7 +51,7 @@ public class InputStreamPump extends InputStream {
private Thread pumpThread;
- private Exception error;
+ private volatile Exception error;
public InputStreamPump(InputStream source, final Pump pump) throws IOException {
this.source = source;
@@ -76,6 +84,13 @@ public class InputStreamPump extends InputStream {
void run(InputStream in) throws Exception;
}
+ /**
+ *
+ * @return exception which has occurred in the pump thread or {@code null}.
+ * @deprecated Rather call {@link #close()}, as otherwise this might be called too early (before the thread finished).
+ * {@code close()} will automatically wrap the potential exception from the pump in an IOException and throws it as well
+ */
+ @Deprecated
public Exception getError() {
return error;
}
@@ -128,6 +143,9 @@ public class InputStreamPump extends InputStream {
} catch (InterruptedException e) {
throw new IOException(e);
}
+ if (error != null) {
+ throw new IOException(error);
+ }
}
@Override
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/PackageInstallIT.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/PackageInstallIT.java
index 303d81b..3282d71 100644
--- a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/PackageInstallIT.java
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/PackageInstallIT.java
@@ -24,16 +24,10 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
-import java.lang.reflect.Field;
import java.security.Principal;
import java.util.Collections;
-import java.util.Properties;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipOutputStream;
import javax.jcr.NodeIterator;
import javax.jcr.Property;
@@ -832,28 +826,7 @@ public class PackageInstallIT extends IntegrationTestBase {
*/
@Test
public void testPackageInstallWith0MtimeZipEntry() throws IOException, RepositoryException, NoSuchFieldException, IllegalAccessException {
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- ZipOutputStream zout = new ZipOutputStream(out);
- Properties p = new Properties();
- p.setProperty("name", TMP_PACKAGE_ID.getName());
- p.setProperty("group", TMP_PACKAGE_ID.getGroup());
- p.setProperty("version", TMP_PACKAGE_ID.getVersionString());
- ZipEntry e = new ZipEntry("META-INF/vault/properties.xml");
-
- Field field = ZipEntry.class.getDeclaredField("xdostime");
- field.setAccessible(true);
- field.setLong(e, 0);
- zout.putNextEntry(e);
- p.storeToXML(zout, "", "utf-8");
- zout.closeEntry();
-
- zout.putNextEntry(new ZipEntry("jcr_root/"));
- zout.closeEntry();
-
- zout.close();
- out.close();
-
- JcrPackage pack = packMgr.upload(new ByteArrayInputStream(out.toByteArray()), true);
+ JcrPackage pack = packMgr.upload(getStream("/test-packages/properties-with-0mtime.zip"), true, true);
assertEquals("packageid", TMP_PACKAGE_ID, pack.getDefinition().getId());
}
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/SubPackagesIT.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/SubPackagesIT.java
index bf3912a..8868d7c 100644
--- a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/SubPackagesIT.java
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/SubPackagesIT.java
@@ -92,7 +92,7 @@ public class SubPackagesIT extends IntegrationTestBase {
*/
public String getInstallationPath(PackageId id) {
// make sure we use the one from the test parameter
- return packageRoots[0] + ((JcrPackageRegistry)packMgr.getRegistry()).getRelativeInstallationPath(id) + ".zip";
+ return packageRoots[0] + "/" + ((JcrPackageRegistry)packMgr.getRegistry()).getRelativeInstallationPath(id) + ".zip";
}
/**
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/FSInstallStateTest.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallStateTest.java
similarity index 66%
rename from vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/FSInstallStateTest.java
rename to vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallStateTest.java
index e140146..a96b408 100644
--- a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/FSInstallStateTest.java
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSInstallStateTest.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.jackrabbit.vault.packaging.integration;
+package org.apache.jackrabbit.vault.packaging.registry.impl;
import static org.junit.Assert.assertEquals;
@@ -23,17 +23,21 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.file.Paths;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
+import org.apache.jackrabbit.vault.fs.config.ConfigurationException;
+import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.filter.DefaultPathFilter;
import org.apache.jackrabbit.vault.packaging.Dependency;
import org.apache.jackrabbit.vault.packaging.PackageId;
import org.apache.jackrabbit.vault.packaging.SubPackageHandling;
-import org.apache.jackrabbit.vault.packaging.registry.impl.FSInstallState;
-import org.apache.jackrabbit.vault.packaging.registry.impl.FSPackageStatus;
import org.hamcrest.MatcherAssert;
import org.junit.Test;
import org.xmlunit.matchers.CompareMatcher;
@@ -60,8 +64,7 @@ public class FSInstallStateTest {
Map<PackageId, SubPackageHandling.Option> subs = new HashMap<>();
subs.put(TMP_PACKAGE_ID, SubPackageHandling.Option.ADD);
- FSInstallState state = new FSInstallState(TMP_PACKAGE_ID, FSPackageStatus.EXTRACTED)
- .withFilePath(testFile.toPath())
+ FSInstallState state = new FSInstallState(TMP_PACKAGE_ID, FSPackageStatus.EXTRACTED, testFile.toPath())
.withExternal(true)
.withDependencies(deps)
.withSubPackages(subs)
@@ -77,9 +80,10 @@ public class FSInstallStateTest {
@Test
public void testReadInstallStateNonExistent() throws IOException {
- FSInstallState state = FSInstallState.fromFile(new File("nonexist.xml"));
+ FSInstallState state = FSInstallState.fromFile(Paths.get("nonexisting.xml"));
assertEquals(null, state);
}
+
@Test
public void testReadInstallState() throws IOException {
Set<Dependency> deps = new HashSet<>();
@@ -96,6 +100,29 @@ public class FSInstallStateTest {
assertEquals(subs, state.getSubPackages());
assertEquals(1234L, state.getSize());
assertEquals((Long) 1234L, state.getInstallationTime());
+ }
+ @Test
+ public void testSaveLoad() throws IOException, ConfigurationException {
+ PackageId packageId = new PackageId("group", "name", "1.1.1");
+ FSInstallState fsInstallState = new FSInstallState(packageId, FSPackageStatus.REGISTERED, Paths.get(""));
+ DefaultWorkspaceFilter filter = new DefaultWorkspaceFilter();
+ PathFilterSet pathFilterSet = new PathFilterSet("/apps/mytest");
+ pathFilterSet.addExclude(new DefaultPathFilter("/apps/mytest/exclude"));
+ pathFilterSet.addInclude(new DefaultPathFilter("/apps/mytest/include"));
+ pathFilterSet.seal();
+ filter.add(pathFilterSet);
+ fsInstallState.withFilter(filter);
+ fsInstallState.withDependencies(Collections.singleton(new Dependency(new PackageId("group", "other", "1.0.0"))));
+ fsInstallState.withInstallTime(1234L);
+ fsInstallState.withSize(333);
+ fsInstallState.withSubPackages(Collections.singletonMap(new PackageId("group", "subpackage", "1.0.0"), SubPackageHandling.Option.FORCE_EXTRACT));
+ try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
+ fsInstallState.save(output);
+ try (InputStream input = new ByteArrayInputStream(output.toByteArray())) {
+ FSInstallState fsInstallState2 = FSInstallState.fromStream(input, "systemId");
+ assertEquals(fsInstallState, fsInstallState2);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/FSPackageRegistryIT.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageRegistryIT.java
similarity index 90%
rename from vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/FSPackageRegistryIT.java
rename to vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageRegistryIT.java
index 34d9902..9ec6887 100644
--- a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/integration/FSPackageRegistryIT.java
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageRegistryIT.java
@@ -15,18 +15,24 @@
* limitations under the License.
*/
-package org.apache.jackrabbit.vault.packaging.integration;
+package org.apache.jackrabbit.vault.packaging.registry.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.io.File;
import java.io.IOException;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.UUID;
import javax.jcr.RepositoryException;
@@ -41,27 +47,26 @@ import org.apache.jackrabbit.vault.packaging.NoSuchPackageException;
import org.apache.jackrabbit.vault.packaging.PackageException;
import org.apache.jackrabbit.vault.packaging.PackageExistsException;
import org.apache.jackrabbit.vault.packaging.PackageId;
+import org.apache.jackrabbit.vault.packaging.VaultPackage;
+import org.apache.jackrabbit.vault.packaging.integration.IntegrationTestBase;
import org.apache.jackrabbit.vault.packaging.registry.DependencyReport;
import org.apache.jackrabbit.vault.packaging.registry.ExecutionPlan;
import org.apache.jackrabbit.vault.packaging.registry.ExecutionPlanBuilder;
import org.apache.jackrabbit.vault.packaging.registry.PackageTask;
-import org.apache.jackrabbit.vault.packaging.registry.RegisteredPackage;
import org.apache.jackrabbit.vault.packaging.registry.PackageTask.Type;
-import org.apache.jackrabbit.vault.packaging.registry.impl.FSInstallState;
-import org.apache.jackrabbit.vault.packaging.registry.impl.FSPackageRegistry;
-import org.apache.jackrabbit.vault.packaging.registry.impl.FSPackageStatus;
-import org.apache.jackrabbit.vault.packaging.registry.impl.InstallationScope;
+import org.apache.jackrabbit.vault.packaging.registry.RegisteredPackage;
+import org.apache.jackrabbit.vault.packaging.registry.impl.FSPackageRegistry.Config;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.Mockito;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.converter.Converter;
+import org.osgi.util.converter.Converters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-
/**
* Test the Package registry interface
*/
@@ -81,6 +86,11 @@ public class FSPackageRegistryIT extends IntegrationTestBase {
"/tmp/foo"
};
+ private static final PackageId TEST_PACKAGE_ID = new PackageId("test", "test-package-with-etc", "1.0");
+
+ @Rule
+ public TemporaryFolder tmpFolder = new TemporaryFolder();
+
private FSPackageRegistry registry;
private File registryHome;
@@ -208,6 +218,12 @@ public class FSPackageRegistryIT extends IntegrationTestBase {
assertTrue("file should still exist", file.exists());
registry.register(file, true);
file.delete();
+ // make sure package is still accessible after original has been deleted
+ try (RegisteredPackage registeredPackage = registry.open(id)) {
+ try (VaultPackage pack = registeredPackage.getPackage()) {
+ assertNotEquals(file, pack.getFile());
+ }
+ }
}
/**
@@ -237,6 +253,7 @@ public class FSPackageRegistryIT extends IntegrationTestBase {
/**
* registers a file as external package twice with
*/
+ @SuppressWarnings("deprecation")
@Test
public void testRegisterExternalFileTwiceFailsLoadedRegistry() throws IOException, PackageException {
File file = getTempFile("/test-packages/tmp.zip");
@@ -312,11 +329,7 @@ public class FSPackageRegistryIT extends IntegrationTestBase {
assertFalse(registry.open(PACKAGE_ID_SUB_A).isInstalled());
assertFalse(registry.open(PACKAGE_ID_SUB_B).isInstalled());
}
-
-
-
- @SuppressWarnings("deprecation")
@Test
public void testInstallExternalUnScoped() throws IOException, PackageException, RepositoryException, org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException {
File file = getTempFile("/test-packages/mixed_package.zip");
@@ -337,7 +350,6 @@ public class FSPackageRegistryIT extends IntegrationTestBase {
checkFiltered(CONTENT_PATHS, new String[] {}, listener.paths);
}
- @SuppressWarnings("deprecation")
@Test
public void testInstallExternalContentScoped() throws IOException, PackageException, RepositoryException, org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException {
File file = getTempFile("/test-packages/mixed_package.zip");
@@ -357,14 +369,12 @@ public class FSPackageRegistryIT extends IntegrationTestBase {
checkFiltered(CONTENT_PATHS, APPLICATION_PATHS, listener.paths);
}
- @SuppressWarnings("deprecation")
private void cleanPaths(String[] paths) throws IOException, RepositoryException, org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException {
for (String path : paths) {
clean(path);
}
}
- @SuppressWarnings("deprecation")
@Test
public void testInstallExternalApplicationScoped() throws IOException, PackageException, RepositoryException, org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException {
File file = getTempFile("/test-packages/mixed_package.zip");
@@ -629,7 +639,7 @@ public class FSPackageRegistryIT extends IntegrationTestBase {
assertTrue("Installation time for idC too early", registry.open(idC).getInstallationTime().compareTo(before) >= 0);
}
-
+
@Test
public void testUnsupportedUninstall() throws Exception {
PackageId idC = registry.register(getStream(TEST_PACKAGE_C_10), false);
@@ -658,27 +668,41 @@ public class FSPackageRegistryIT extends IntegrationTestBase {
//expected
}
}
-
+
@Test
- public void testNonMetaXmlFile() throws Exception {
- PackageId idC = registry.register(getStream(TEST_PACKAGE_C_10), false);
+ public void testInvalidMetaXmlFile() throws Exception {
+ getFreshRegistryWithDefaultConstructor("test-package.zip", "invalid-metadata.xml");
+ assertNull(registry.getInstallState(TEST_PACKAGE_ID));
+ }
- assertEquals(idC, registry.getInstallState(idC).getPackageId());
- assertEquals(FSPackageStatus.REGISTERED, registry.getInstallState(idC).getStatus());
+ @Test
+ public void testCacheInitializedAfterOSGiActivate() throws IOException {
+ new FSPackageRegistry();
+ getFreshRegistryWithDefaultConstructor("test-package.zip", "test-package.xml");
+ assertTrue(registry.contains(TEST_PACKAGE_ID));
+ assertEquals(Collections.singleton(TEST_PACKAGE_ID), registry.packages());
+ }
+
+ private void getFreshRegistryWithDefaultConstructor(String packageName, String packageMetadataName) throws IOException {
+ if (this.registryHome != null && this.registryHome.exists()) {
+ this.registryHome.delete();
+ }
+ this.registryHome = new File(DIR_REGISTRY_HOME, UUID.randomUUID().toString());
+ FileUtils.copyInputStreamToFile(getClass().getResourceAsStream(packageName), new File(this.registryHome, "package1.zip"));
+ FileUtils.copyInputStreamToFile(getClass().getResourceAsStream(packageMetadataName), new File(this.registryHome, "package1.zip.xml"));
- Field stateCache = registry.getClass().getDeclaredField("stateCache");
- stateCache.setAccessible(true);
- stateCache.set(registry, new HashMap<String, FSInstallState>() );
- Method getPackageMetaDataFile = registry.getClass().getDeclaredMethod("getPackageMetaDataFile", new Class<?>[]{idC.getClass()});
- getPackageMetaDataFile.setAccessible(true);
- Method loadPackageCache = registry.getClass().getDeclaredMethod("loadPackageCache");
- loadPackageCache.setAccessible(true);
- File metaFile = (File)getPackageMetaDataFile.invoke(registry, idC);
- FileUtils.copyToFile(getStream("repository.xml"), metaFile);
- loadPackageCache.invoke(registry);
- assertEquals(FSPackageStatus.NOTREGISTERED, registry.getInstallState(idC).getStatus());
+ BundleContext context = Mockito.mock(BundleContext.class);
+ Mockito.when(context.getProperty(FSPackageRegistry.REPOSITORY_HOME)).thenReturn(DIR_REGISTRY_HOME.toString());
+ Converter converter = Converters.standardConverter();
+ Map<String, Object> map = new HashMap<>();
+ map.put("homePath", registryHome.getName());
+ map.put("authIdsForHookExecution", new String[0]);
+ map.put("authIdsForRootInstallation", new String[0]);
+ Config config = converter.convert(map).to(Config.class);
+ registry.activate(context, config);
}
-
+
+ @SuppressWarnings("deprecation")
private void getFreshRegistry(InstallationScope... scope) throws IOException {
if (this.registryHome != null && this.registryHome.exists()) {
this.registryHome.delete();
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageRegistryTest.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageRegistryTest.java
new file mode 100644
index 0000000..880d326
--- /dev/null
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSPackageRegistryTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.jackrabbit.vault.packaging.registry.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.jackrabbit.vault.packaging.NoSuchPackageException;
+import org.apache.jackrabbit.vault.packaging.PackageExistsException;
+import org.apache.jackrabbit.vault.packaging.PackageId;
+import org.apache.jackrabbit.vault.packaging.registry.impl.FSPackageRegistry.Config;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.mockito.Mockito;
+import org.osgi.framework.BundleContext;
+import org.osgi.util.converter.Converter;
+import org.osgi.util.converter.Converters;
+
+public class FSPackageRegistryTest {
+
+ @Rule
+ public TemporaryFolder tmpFolder = new TemporaryFolder();
+
+ private static final PackageId TEST_PACKAGE_ID = new PackageId("test", "test-package-with-etc", "1.0");
+
+ private void copyResourceStreamToFile(Path targetFile, String name) throws IOException {
+ try (InputStream in = getClass().getResourceAsStream(name)) {
+ Files.copy(in, targetFile, StandardCopyOption.REPLACE_EXISTING);
+ }
+ }
+
+ private Path getTempRegistryHomeWithPackage(String packageName, String packageMetadataName) throws IOException {
+ Path tmpDir = tmpFolder.newFolder().toPath();
+ copyResourceStreamToFile(tmpDir.resolve("package1.zip"), packageName);
+ copyResourceStreamToFile(tmpDir.resolve("package1.zip.xml"), packageMetadataName);
+ return tmpDir;
+ }
+
+ @Test
+ public void testRegisterAndRemove() throws IOException, PackageExistsException, NoSuchPackageException {
+ FSPackageRegistry registry = createRegistryWithDefaultConstructor(tmpFolder.newFolder().toPath());
+ try (InputStream in = getClass().getResourceAsStream("test-package.zip")) {
+ registry.register(in, false);
+ }
+ assertTrue(registry.contains(TEST_PACKAGE_ID));
+ registry.remove(TEST_PACKAGE_ID);
+ assertFalse(registry.contains(TEST_PACKAGE_ID));
+ }
+
+ @Test
+ public void testCacheInitializedAfterOSGiActivate() throws IOException {
+ new FSPackageRegistry();
+ Path registryHomeDir = getTempRegistryHomeWithPackage("test-package.zip", "test-package.xml");
+ FSPackageRegistry registry = createRegistryWithDefaultConstructor(registryHomeDir);
+ assertTrue(registry.contains(TEST_PACKAGE_ID));
+ assertEquals(Collections.singleton(TEST_PACKAGE_ID), registry.packages());
+ }
+
+ private FSPackageRegistry createRegistryWithDefaultConstructor(Path homePath) throws IOException {
+ FSPackageRegistry registry = new FSPackageRegistry();
+ BundleContext context = Mockito.mock(BundleContext.class);
+ Mockito.when(context.getProperty(FSPackageRegistry.REPOSITORY_HOME)).thenReturn(tmpFolder.getRoot().toString());
+ Converter converter = Converters.standardConverter();
+ Map<String, Object> map = new HashMap<>();
+ map.put("homePath", homePath.toString());
+ map.put("authIdsForHookExecution", new String[0]);
+ map.put("authIdsForRootInstallation", new String[0]);
+ Config config = converter.convert(map).to(Config.class);
+ registry.activate(context, config);
+ return registry;
+ }
+}
diff --git a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSRegisteredPackageTest.java b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSRegisteredPackageTest.java
index 9824490..222ce8d 100644
--- a/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSRegisteredPackageTest.java
+++ b/vault-core/src/test/java/org/apache/jackrabbit/vault/packaging/registry/impl/FSRegisteredPackageTest.java
@@ -29,6 +29,7 @@ import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.vault.packaging.NoSuchPackageException;
import org.apache.jackrabbit.vault.packaging.PackageId;
import org.apache.jackrabbit.vault.packaging.VaultPackage;
import org.apache.jackrabbit.vault.packaging.impl.HollowVaultPackage;
@@ -58,7 +59,7 @@ public class FSRegisteredPackageTest {
}
}
- private FSPackageRegistry newRegistry(File packageFile) throws IOException {
+ private FSPackageRegistry newRegistry(File packageFile) throws IOException, NoSuchPackageException {
FSPackageRegistry registry = Mockito.mock(FSPackageRegistry.class);
Mockito.when(registry.openPackageFile(DUMMY_ID)).thenReturn(safeLoadVaultPackage(packageFile));
return registry;
@@ -72,7 +73,7 @@ public class FSRegisteredPackageTest {
}
@Test
- public void testGetPackageFromNonTruncatedFile() throws IOException {
+ public void testGetPackageFromNonTruncatedFile() throws IOException, NoSuchPackageException {
File packageFile = getTempFile("test-package.zip");
try (RegisteredPackage regPack = new FSRegisteredPackage(newRegistry(packageFile), newInstallState(packageFile));
VaultPackage vltPack = regPack.getPackage()) {
@@ -86,7 +87,7 @@ public class FSRegisteredPackageTest {
}
@Test
- public void testGetPackageFromTruncatedFile() throws IOException {
+ public void testGetPackageFromTruncatedFile() throws IOException, NoSuchPackageException {
File packageFile = getTempFile("test-package-truncated.zip");
try (RegisteredPackage regPack = new FSRegisteredPackage(newRegistry(packageFile), newInstallState(packageFile));
VaultPackage vltPack = regPack.getPackage()) {
diff --git a/vault-core/src/test/resources/org/apache/jackrabbit/vault/packaging/registry/impl/invalid-metadata.xml b/vault-core/src/test/resources/org/apache/jackrabbit/vault/packaging/registry/impl/invalid-metadata.xml
new file mode 100644
index 0000000..c31e942
--- /dev/null
+++ b/vault-core/src/test/resources/org/apache/jackrabbit/vault/packaging/registry/impl/invalid-metadata.xml
@@ -0,0 +1,174 @@
+<?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.
+-->
+
+<!DOCTYPE Repository
+ PUBLIC "-//The Apache Software Foundation//DTD Jackrabbit 2.0//EN"
+ "http://jackrabbit.apache.org/dtd/repository-2.0.dtd">
+
+<!-- Example Repository Configuration File
+ Used by
+ - org.apache.jackrabbit.core.config.RepositoryConfigTest.java
+ -
+-->
+<Repository>
+ <!--
+ virtual file system where the repository stores global state
+ (e.g. registered namespaces, custom node types, etc.)
+ -->
+ <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+ <param name="path" value="${rep.home}/repository"/>
+ </FileSystem>
+
+ <!--
+ data store configuration
+ -->
+ <DataStore class="org.apache.jackrabbit.core.data.FileDataStore"/>
+
+ <!--
+ security configuration
+ -->
+ <Security appName="Jackrabbit">
+
+ <SecurityManager class="org.apache.jackrabbit.core.UserPerWorkspaceSecurityManager">
+ <!--
+ optional user manager configuration
+ -->
+ <UserManager class="org.apache.jackrabbit.core.security.user.UserPerWorkspaceUserManager">
+ <param name="usersPath" value="/home/users"/>
+ <param name="groupsPath" value="/home/groups"/>
+ </UserManager>
+
+ <!--
+ optional workspace access manager configuration
+ -->
+ </SecurityManager>
+
+ <!--
+ access manager:
+ class: FQN of class implementing the AccessManager interface
+ -->
+ <AccessManager class="org.apache.jackrabbit.core.security.DefaultAccessManager">
+ <!-- <param name="config" value="${rep.home}/access.xml"/> -->
+ </AccessManager>
+
+ <LoginModule class="org.apache.jackrabbit.core.security.authentication.DefaultLoginModule">
+ <!--
+ anonymous user name ('anonymous' is the default value)
+ -->
+ <param name="anonymousId" value="anonymous"/>
+ <!--
+ administrator user id (default value if param is missing is 'admin')
+ -->
+ <param name="adminId" value="admin"/>
+ </LoginModule>
+ </Security>
+
+ <!--
+ location of workspaces root directory and name of default workspace
+ -->
+ <Workspaces rootPath="${rep.home}/workspaces" defaultWorkspace="default"/>
+ <!--
+ workspace configuration template:
+ used to create the initial workspace if there's no workspace yet
+ -->
+ <Workspace name="${wsp.name}">
+ <!--
+ virtual file system of the workspace:
+ class: FQN of class implementing the FileSystem interface
+ -->
+ <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+ <param name="path" value="${wsp.home}"/>
+ </FileSystem>
+ <!--
+ persistence manager of the workspace:
+ class: FQN of class implementing the PersistenceManager interface
+ -->
+ <PersistenceManager class="org.apache.jackrabbit.core.persistence.pool.DerbyPersistenceManager">
+ <param name="url" value="jdbc:derby:${wsp.home}/db;create=true"/>
+ <param name="schemaObjectPrefix" value="${wsp.name}_"/>
+ </PersistenceManager>
+ <!--
+ Search index and the file system it uses.
+ class: FQN of class implementing the QueryHandler interface
+ -->
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path" value="${wsp.home}/index"/>
+ <param name="supportHighlighting" value="true"/>
+ </SearchIndex>
+
+ <!--
+ Workspace security configuration
+ -->
+ <WorkspaceSecurity>
+ <AccessControlProvider class="org.apache.jackrabbit.core.security.authorization.acl.ACLProvider">
+ <!-- param name="omit-default-permission" value="true"/-->
+ <param name="allow-unknown-principals" value="true"/>
+ </AccessControlProvider>
+ </WorkspaceSecurity>
+
+ <!--
+ XML Import configuration of the workspace
+ -->
+ <Import>
+ <ProtectedItemImporter class="org.apache.jackrabbit.core.xml.AccessControlImporter"/>
+ <ProtectedItemImporter class="org.apache.jackrabbit.core.security.user.UserImporter">
+ <param name="importBehavior" value="besteffort"/>
+ </ProtectedItemImporter>
+ </Import>
+ </Workspace>
+
+ <!--
+ Configures the versioning
+ -->
+ <Versioning rootPath="${rep.home}/version">
+ <!--
+ Configures the filesystem to use for versioning for the respective
+ persistence manager
+ -->
+ <FileSystem class="org.apache.jackrabbit.core.fs.local.LocalFileSystem">
+ <param name="path" value="${rep.home}/version" />
+ </FileSystem>
+
+ <!--
+ Configures the persistence manager to be used for persisting version state.
+ Please note that the current versioning implementation is based on
+ a 'normal' persistence manager, but this could change in future
+ implementations.
+ -->
+ <PersistenceManager class="org.apache.jackrabbit.core.persistence.pool.DerbyPersistenceManager">
+ <param name="url" value="jdbc:derby:${rep.home}/version/db;create=true"/>
+ <param name="schemaObjectPrefix" value="version_"/>
+ </PersistenceManager>
+ </Versioning>
+
+ <!--
+ Search index for content that is shared repository wide
+ (/jcr:system tree, contains mainly versions)
+ -->
+ <SearchIndex class="org.apache.jackrabbit.core.query.lucene.SearchIndex">
+ <param name="path" value="${rep.home}/repository/index"/>
+ <param name="supportHighlighting" value="true"/>
+ </SearchIndex>
+
+ <!--
+ Run with a cluster journal
+ -->
+ <Cluster id="node1">
+ <Journal class="org.apache.jackrabbit.core.journal.MemoryJournal"/>
+ </Cluster>
+</Repository>
diff --git a/vault-core/src/test/resources/org/apache/jackrabbit/vault/packaging/registry/impl/test-package.xml b/vault-core/src/test/resources/org/apache/jackrabbit/vault/packaging/registry/impl/test-package.xml
new file mode 100644
index 0000000..d09709e
--- /dev/null
+++ b/vault-core/src/test/resources/org/apache/jackrabbit/vault/packaging/registry/impl/test-package.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<registryMetadata packageid="test:test-package-with-etc:1.0" size="-1" filepath="/var/folders/rm/vlg2h6m16mb0f65djmnb12xr0000gq/T/junit1443483155243547543/junit9008025097740758307/home/test/test-package-with-etc-1.0.zip" external="false" packagestatus="registered">
+ <workspacefilter>
+ <filter root="/etc">
+ <rule include="/etc"/>
+ <rule include="/etc/clientlibs"/>
+ <rule include="/etc/clientlibs/granite"/>
+ <rule include="/etc/clientlibs/granite/test(/.*)?"/>
+ </filter>
+ </workspacefilter>
+ <packageproperties createdBy="admin" name="test-package-with-etc" lastModified="2017-05-16T17:23:18.391+09:00" lastModifiedBy="admin" acHandling="MERGE_PRESERVE" created="2017-05-16T17:23:18.655+09:00" buildCount="1" version="1.0" dependencies="" packageFormatVersion="2" group="test" lastWrappedBy="admin"/>
+</registryMetadata>
diff --git a/vault-core/src/test/resources/test-packages/properties-with-0mtime.zip b/vault-core/src/test/resources/test-packages/properties-with-0mtime.zip
new file mode 100644
index 0000000..fc43677
Binary files /dev/null and b/vault-core/src/test/resources/test-packages/properties-with-0mtime.zip differ