You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by pa...@apache.org on 2019/05/09 14:55:31 UTC
[sling-org-apache-sling-jcr-base] 01/01: SLING-8411: Provide a way
to bifurcat a repository path to a provider mount.
This is an automated email from the ASF dual-hosted git repository.
pauls pushed a commit to branch SLING-8411
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-base.git
commit 63e79550819e48d436b53903862d68253a501e40
Author: Karl Pauls <ka...@gmail.com>
AuthorDate: Thu May 9 16:55:12 2019 +0200
SLING-8411: Provide a way to bifurcat a repository path to a provider mount.
---
pom.xml | 8 +-
.../sling/jcr/base/AbstractSlingRepository2.java | 27 +-
.../jcr/base/AbstractSlingRepositoryManager.java | 44 +-
.../jcr/base/internal/mount/ChainedIterator.java | 75 +++
.../internal/mount/ProxyAccessControlManager.java | 107 ++++
.../sling/jcr/base/internal/mount/ProxyItem.java | 135 +++++
.../mount/ProxyJackrabbitAccessControlManager.java | 77 +++
.../internal/mount/ProxyJackrabbitRepository.java | 59 ++
.../internal/mount/ProxyJackrabbitSession.java | 85 +++
.../internal/mount/ProxyJackrabbitWorkspace.java | 42 ++
.../sling/jcr/base/internal/mount/ProxyLock.java | 75 +++
.../internal/mount/ProxyNamespaceRegistry.java | 67 +++
.../sling/jcr/base/internal/mount/ProxyNode.java | 413 ++++++++++++++
.../base/internal/mount/ProxyNodeTypeManager.java | 111 ++++
.../base/internal/mount/ProxyPrivilegeManager.java | 62 +++
.../jcr/base/internal/mount/ProxyProperty.java | 157 ++++++
.../sling/jcr/base/internal/mount/ProxyQuery.java | 233 ++++++++
.../jcr/base/internal/mount/ProxyQueryManager.java | 55 ++
.../base/internal/mount/ProxyQueryObjectModel.java | 47 ++
.../mount/ProxyQueryObjectModelFactory.java | 183 ++++++
.../jcr/base/internal/mount/ProxyQueryResult.java | 46 ++
.../jcr/base/internal/mount/ProxyRepository.java | 133 +++++
.../jcr/base/internal/mount/ProxySession.java | 615 +++++++++++++++++++++
.../jcr/base/internal/mount/ProxyUserManager.java | 123 +++++
.../jcr/base/internal/mount/ProxyWorkspace.java | 168 ++++++
.../jcr/base/internal/mount/ProxyWrapper.java | 40 ++
.../apache/sling/jcr/base/spi/RepositoryMount.java | 27 +
.../apache/sling/jcr/base/spi/package-info.java | 27 +
28 files changed, 3231 insertions(+), 10 deletions(-)
diff --git a/pom.xml b/pom.xml
index 453b538..bab9c73 100644
--- a/pom.xml
+++ b/pom.xml
@@ -116,7 +116,7 @@
<dependency>
<groupId>org.apache.jackrabbit</groupId>
<artifactId>jackrabbit-api</artifactId>
- <version>2.0.0</version>
+ <version>2.13.4</version>
<scope>provided</scope>
</dependency>
<dependency>
@@ -131,6 +131,12 @@
<version>1.3.6</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.jackrabbit</groupId>
+ <artifactId>oak-commons</artifactId>
+ <version>1.8.2</version>
+ <scope>provided</scope>
+ </dependency>
<!-- OSGi Libraries -->
<dependency>
diff --git a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java
index d6d7307..56cbf81 100644
--- a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java
+++ b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java
@@ -36,6 +36,7 @@ import javax.jcr.Value;
import javax.security.auth.Subject;
import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.base.internal.mount.ProxyRepository;
import org.apache.sling.serviceusermapping.ServiceUserMapper;
import org.osgi.annotation.versioning.ProviderType;
import org.osgi.framework.Bundle;
@@ -159,14 +160,22 @@ public abstract class AbstractSlingRepository2 implements SlingRepository {
private Session createServiceSession(Bundle usingBundle, String subServiceName, String workspaceName) throws RepositoryException {
final ServiceUserMapper serviceUserMapper = this.getSlingRepositoryManager().getServiceUserMapper();
if (serviceUserMapper != null) {
+ Session session = null;
final Iterable<String> principalNames = serviceUserMapper.getServicePrincipalNames(usingBundle, subServiceName);
if (principalNames != null) {
- return createServiceSession(principalNames, workspaceName);
+ session = createServiceSession(principalNames, workspaceName);
}
-
- final String userName = serviceUserMapper.getServiceUserID(usingBundle, subServiceName);
- if (userName != null) {
- return createServiceSession(userName, workspaceName);
+ else
+ {
+ final String userName = serviceUserMapper.getServiceUserID(usingBundle, subServiceName);
+ if (userName != null)
+ {
+ session = createServiceSession(userName, workspaceName);
+ }
+ }
+ if (session != null) {
+ Repository repository = getRepository();
+ return repository instanceof ProxyRepository ? ((ProxyRepository) repository).wrap(session) : session;
}
}
return null;
@@ -196,7 +205,9 @@ public abstract class AbstractSlingRepository2 implements SlingRepository {
Session admin = null;
try {
admin = this.createAdministrativeSession(workspace);
- return admin.impersonate(new SimpleCredentials(serviceUserName, new char[0]));
+ Session result = admin.impersonate(new SimpleCredentials(serviceUserName, new char[0]));
+ Repository repository = getRepository();
+ return repository instanceof ProxyRepository ? ((ProxyRepository) repository).wrap(result) : result;
} finally {
if (admin != null) {
admin.logout();
@@ -445,7 +456,9 @@ public abstract class AbstractSlingRepository2 implements SlingRepository {
}
logger.debug("SlingRepository.loginAdministrative is deprecated. Please use SlingRepository.loginService.");
- return createAdministrativeSession(workspace);
+ Session result = createAdministrativeSession(workspace);
+ Repository repository = getRepository();
+ return repository instanceof ProxyRepository ? ((ProxyRepository) repository).wrap(result) : result;
}
// Remaining Repository service methods all backed by the actual
diff --git a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java
index 761c956..8e54c49 100644
--- a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java
+++ b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java
@@ -21,20 +21,27 @@ package org.apache.sling.jcr.base;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Dictionary;
+import java.util.HashSet;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import javax.jcr.Repository;
+import org.apache.jackrabbit.api.JackrabbitRepository;
import org.apache.sling.jcr.api.SlingRepository;
import org.apache.sling.jcr.api.SlingRepositoryInitializer;
import org.apache.sling.jcr.base.internal.loader.Loader;
import org.apache.sling.jcr.base.internal.LoginAdminWhitelist;
+import org.apache.sling.jcr.base.internal.mount.ProxyJackrabbitRepository;
+import org.apache.sling.jcr.base.internal.mount.ProxyRepository;
+import org.apache.sling.jcr.base.spi.RepositoryMount;
import org.apache.sling.serviceusermapping.ServiceUserMapper;
import org.osgi.annotation.versioning.ProviderType;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
@@ -116,6 +123,8 @@ public abstract class AbstractSlingRepositoryManager {
private volatile boolean stopRequested;
+ volatile ServiceTracker<RepositoryMount, RepositoryMount> mountTracker;
+
/**
* Returns the default workspace, which may be <code>null</code> meaning to
* use the repository provided default workspace.
@@ -283,7 +292,30 @@ public abstract class AbstractSlingRepositoryManager {
* @return The repository
*/
protected final Repository getRepository() {
- return repository;
+ ServiceReference<RepositoryMount> ref = mountTracker != null ? mountTracker.getServiceReference() : null;
+
+ Repository mountRepo = (ref != null ? mountTracker.getService(ref) : null);
+ Object mounts = ref != null ? ref.getProperty(RepositoryMount.MOUNT_POINTS_KEY) : null;
+ Set<String> mountPoints = new HashSet<>();
+
+ if (mounts != null) {
+ if (mounts instanceof String[]) {
+ for (String mount : ((String[]) mounts)) {
+ mountPoints.add(mount);
+ }
+ }
+ else {
+ mountPoints.add(mounts.toString());
+ }
+ }
+ else {
+ mountPoints.add("/content/jcrmount");
+ }
+ return mountRepo != null ?
+ repository instanceof JackrabbitRepository ?
+ new ProxyJackrabbitRepository((JackrabbitRepository) repository, (JackrabbitRepository) mountRepo, mountPoints) :
+ new ProxyRepository(repository, mountRepo, mountPoints) :
+ repository;
}
/**
@@ -393,6 +425,9 @@ public abstract class AbstractSlingRepositoryManager {
this.defaultWorkspace = config.defaultWorkspace;
this.disableLoginAdministrative = config.disableLoginAdministrative;
+ this.mountTracker = new ServiceTracker<>(this.bundleContext, RepositoryMount.class, null);
+ this.mountTracker.open();
+
this.repoInitializerTracker = new ServiceTracker<SlingRepositoryInitializer, SlingRepositoryInitializerInfo>(bundleContext, SlingRepositoryInitializer.class,
new ServiceTrackerCustomizer<SlingRepositoryInitializer, SlingRepositoryInitializerInfo>() {
@@ -574,6 +609,11 @@ public abstract class AbstractSlingRepositoryManager {
startupThread = null;
}
+ if (this.mountTracker != null) {
+ this.mountTracker.close();
+ this.mountTracker = null;
+ }
+
// ensure the repository is really disposed off
if (repository != null || isRepositoryServiceRegistered()) {
log.info("stop: Repository still running, forcing shutdown");
@@ -604,7 +644,7 @@ public abstract class AbstractSlingRepositoryManager {
this.destroy(this.masterSlingRepository);
try {
- disposeRepository(oldRepo);
+ disposeRepository(oldRepo instanceof ProxyRepository ? ((ProxyRepository) oldRepo).jcr : oldRepo);
} catch (Throwable t) {
log.info("stop: Uncaught problem disposing the repository", t);
}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ChainedIterator.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ChainedIterator.java
new file mode 100644
index 0000000..015a920
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ChainedIterator.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class ChainedIterator<T> implements Iterator<T> {
+
+ private T nextElement;
+
+ private final Iterator<Iterator<T>> iterators;
+
+ private Iterator<T> currentIterator;
+
+ public ChainedIterator(final Iterator<Iterator<T>> iterators) {
+ this.iterators = iterators;
+ }
+
+ protected T seek() {
+ while (true) {
+ if (currentIterator == null) {
+ if (!iterators.hasNext()) {
+ return null;
+ }
+ currentIterator = iterators.next();
+ continue;
+ }
+ if (currentIterator.hasNext()) {
+ return currentIterator.next();
+ } else {
+ currentIterator = null;
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (nextElement == null) {
+ nextElement = seek();
+ }
+ return nextElement != null;
+ }
+
+ @Override
+ public T next() {
+ if (nextElement == null && !hasNext()) {
+ throw new NoSuchElementException();
+ }
+ final T result = nextElement;
+ nextElement = null;
+ return result;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyAccessControlManager.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyAccessControlManager.java
new file mode 100644
index 0000000..e82d362
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyAccessControlManager.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.lock.LockException;
+import javax.jcr.security.AccessControlException;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.security.AccessControlPolicy;
+import javax.jcr.security.AccessControlPolicyIterator;
+import javax.jcr.security.Privilege;
+import javax.jcr.version.VersionException;
+
+public class ProxyAccessControlManager<T extends AccessControlManager> extends ProxyWrapper<T> implements AccessControlManager {
+ final T mount;
+
+ public ProxyAccessControlManager(ProxySession<?> mountSession, T delegate, T mount) {
+ super(mountSession, delegate);
+ this.mount = mount;
+ }
+
+ public Privilege[] getSupportedPrivileges(String absPath) throws PathNotFoundException, RepositoryException {
+ if (mountSession.isMount(absPath)) {
+ return mount.getSupportedPrivileges(absPath);
+ }
+ return delegate.getSupportedPrivileges(absPath);
+ }
+
+ public Privilege privilegeFromName(String privilegeName) throws AccessControlException, RepositoryException {
+ try {
+ return delegate.privilegeFromName(privilegeName);
+ } catch (AccessControlException ex) {
+ return mount.privilegeFromName(privilegeName);
+ }
+ }
+
+ public boolean hasPrivileges(String absPath, Privilege[] privileges) throws PathNotFoundException, RepositoryException {
+ if (mountSession.isMount(absPath)) {
+ return mount.hasPrivileges(absPath, privileges);
+ }
+ return delegate.hasPrivileges(absPath, privileges);
+ }
+
+ public Privilege[] getPrivileges(String absPath) throws PathNotFoundException, RepositoryException {
+ if (mountSession.isMount(absPath)) {
+ return mount.getPrivileges(absPath);
+ }
+ return delegate.getPrivileges(absPath);
+ }
+
+ public AccessControlPolicy[] getPolicies(String absPath) throws PathNotFoundException, AccessDeniedException, RepositoryException {
+ if (mountSession.isMount(absPath)) {
+ return mount.getPolicies(absPath);
+ }
+ return delegate.getPolicies(absPath);
+ }
+
+ public AccessControlPolicy[] getEffectivePolicies(String absPath) throws PathNotFoundException, AccessDeniedException, RepositoryException {
+ if (mountSession.isMount(absPath)) {
+ return mount.getEffectivePolicies(absPath);
+ }
+ return delegate.getEffectivePolicies(absPath);
+ }
+
+ public AccessControlPolicyIterator getApplicablePolicies(String absPath) throws PathNotFoundException, AccessDeniedException, RepositoryException {
+ if (mountSession.isMount(absPath)) {
+ return mount.getApplicablePolicies(absPath);
+ }
+ return delegate.getApplicablePolicies(absPath);
+ }
+
+ public void setPolicy(String absPath, AccessControlPolicy policy) throws PathNotFoundException, AccessControlException, AccessDeniedException, LockException, VersionException, RepositoryException {
+ if (mountSession.isMountParent(absPath) || mountSession.isMount(absPath)) {
+ mount.setPolicy(absPath, policy);
+ }
+ if (!mountSession.isMount(absPath)) {
+ delegate.setPolicy(absPath, policy);
+ }
+ }
+
+ public void removePolicy(String absPath, AccessControlPolicy policy) throws PathNotFoundException, AccessControlException, AccessDeniedException, LockException, VersionException, RepositoryException {
+ if (mountSession.isMountParent(absPath) || mountSession.isMount(absPath)) {
+ mount.removePolicy(absPath, policy);
+ }
+ if (!mountSession.isMount(absPath)) {
+ delegate.removePolicy(absPath, policy);
+ }
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyItem.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyItem.java
new file mode 100644
index 0000000..1207100
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyItem.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.Item;
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.ItemVisitor;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.ReferentialIntegrityException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.version.VersionException;
+
+import org.apache.jackrabbit.oak.commons.PathUtils;
+
+public class ProxyItem<T extends Item> extends ProxyWrapper<T> implements Item {
+ public ProxyItem(ProxySession mountSession, T delegate) {
+ super(mountSession, delegate);
+ }
+
+ @Override
+ public String getPath() throws RepositoryException {
+ return delegate.getPath();
+ }
+
+ @Override
+ public String getName() throws RepositoryException {
+ return delegate.getName();
+ }
+
+ @Override
+ public Item getAncestor(int depth) throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+ return this.mountSession.getItem(this.delegate.getAncestor(depth).getPath());
+ }
+
+ @Override
+ public Node getParent() throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+
+ String parentPath;
+
+ try {
+ parentPath = this.delegate.getParent().getPath();
+ } catch (Exception ex) {
+ if (ex instanceof AccessDeniedException) {
+ throw (AccessDeniedException) ex;
+ }
+ parentPath = PathUtils.getParentPath(this.delegate.getPath());
+ }
+
+ return this.mountSession.getNode(parentPath);
+ }
+
+ @Override
+ public int getDepth() throws RepositoryException {
+ return delegate.getDepth();
+ }
+
+ @Override
+ public Session getSession() throws RepositoryException {
+ return mountSession;
+ }
+
+ @Override
+ public boolean isNode() {
+ return delegate.isNode();
+ }
+
+ @Override
+ public boolean isNew() {
+ return delegate.isNew();
+ }
+
+ @Override
+ public boolean isModified() {
+ return delegate.isModified();
+ }
+
+ @Override
+ public boolean isSame(Item otherItem) throws RepositoryException {
+ return delegate.isSame(this.mountSession.unwrap(otherItem));
+ }
+
+ @Override
+ public void accept(final ItemVisitor visitor) throws RepositoryException {
+ delegate.accept(new ItemVisitor() {
+ @Override
+ public void visit(Property property) throws RepositoryException {
+ visitor.visit(mountSession.wrap(property));
+ }
+
+ @Override
+ public void visit(Node node) throws RepositoryException {
+ visitor.visit(mountSession.wrap(node));
+ }
+ });
+ }
+
+ @Override
+ public void save() throws AccessDeniedException, ItemExistsException, ConstraintViolationException, InvalidItemStateException, ReferentialIntegrityException, VersionException, LockException, NoSuchNodeTypeException, RepositoryException {
+ this.mountSession.save();
+ }
+
+ @Override
+ public void refresh(boolean keepChanges) throws InvalidItemStateException, RepositoryException {
+ this.mountSession.refresh(getPath(), delegate, keepChanges);
+ }
+
+ @Override
+ public void remove() throws VersionException, LockException, ConstraintViolationException, AccessDeniedException, RepositoryException {
+ this.mountSession.removeItem(this.getPath());
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitAccessControlManager.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitAccessControlManager.java
new file mode 100644
index 0000000..f810cd8
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitAccessControlManager.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.security.AccessControlException;
+import javax.jcr.security.AccessControlPolicy;
+import javax.jcr.security.Privilege;
+
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlPolicy;
+
+public class ProxyJackrabbitAccessControlManager extends ProxyAccessControlManager<JackrabbitAccessControlManager> implements JackrabbitAccessControlManager {
+ public ProxyJackrabbitAccessControlManager(ProxySession<?> mountSession, JackrabbitAccessControlManager delegate, JackrabbitAccessControlManager mount) {
+ super(mountSession, delegate, mount);
+ }
+
+ public JackrabbitAccessControlPolicy[] getApplicablePolicies(Principal principal) throws AccessDeniedException, AccessControlException, UnsupportedRepositoryOperationException, RepositoryException {
+ List<JackrabbitAccessControlPolicy> result = new ArrayList<>();
+ result.addAll(Arrays.asList(delegate.getApplicablePolicies(principal)));
+ result.addAll(Arrays.asList(mount.getApplicablePolicies(principal)));
+ return result.toArray(new JackrabbitAccessControlPolicy[0]);
+ }
+
+ public JackrabbitAccessControlPolicy[] getPolicies(Principal principal) throws AccessDeniedException, AccessControlException, UnsupportedRepositoryOperationException, RepositoryException {
+ List<JackrabbitAccessControlPolicy> result = new ArrayList<>();
+ result.addAll(Arrays.asList(delegate.getPolicies(principal)));
+ result.addAll(Arrays.asList(mount.getPolicies(principal)));
+ return result.toArray(new JackrabbitAccessControlPolicy[0]);
+ }
+
+ public AccessControlPolicy[] getEffectivePolicies(Set<Principal> principals) throws AccessDeniedException, AccessControlException, UnsupportedRepositoryOperationException, RepositoryException {
+ List<AccessControlPolicy> result = new ArrayList<>();
+ result.addAll(Arrays.asList(delegate.getEffectivePolicies(principals)));
+ result.addAll(Arrays.asList(mount.getEffectivePolicies(principals)));
+ return result.toArray(new AccessControlPolicy[0]);
+ }
+
+ public boolean hasPrivileges(String absPath, Set<Principal> principals, Privilege[] privileges) throws PathNotFoundException, AccessDeniedException, RepositoryException {
+ if (mountSession.isMount(absPath)) {
+ return mount.hasPrivileges(absPath, principals, privileges);
+ }
+ return delegate.hasPrivileges(absPath, principals, privileges);
+ }
+
+ public Privilege[] getPrivileges(String absPath, Set<Principal> principals) throws PathNotFoundException, AccessDeniedException, RepositoryException {
+ if (mountSession.isMount(absPath)) {
+ return mount.getPrivileges(absPath, principals);
+ }
+ return delegate.getPrivileges(absPath, principals);
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitRepository.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitRepository.java
new file mode 100644
index 0000000..42e2c2a
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitRepository.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import org.apache.jackrabbit.api.JackrabbitSession;
+
+public class ProxyJackrabbitRepository extends ProxyRepository<JackrabbitRepository> implements JackrabbitRepository {
+ public ProxyJackrabbitRepository(JackrabbitRepository jcr, JackrabbitRepository mount, Set<String> mountPoint) {
+ super(jcr, mount, mountPoint);
+ }
+
+ @Override
+ public Session login(Credentials credentials, String workspaceName, Map<String, Object> attributes) throws LoginException, NoSuchWorkspaceException, RepositoryException {
+ Session jcrSession = jcr.login(credentials, workspaceName, attributes);
+
+ if (attributes == null) {
+ attributes = new HashMap<>();
+ }
+ attributes.put(ProxyRepository.class.getPackage().getName() + ".PARENT_SESSION", jcrSession);
+
+ Session mountSession = mount.login(credentials, workspaceName, attributes);
+
+ return jcrSession instanceof JackrabbitSession ?
+ new ProxyJackrabbitSession(this, (JackrabbitSession) jcrSession, mountSession, this.mountPoints) :
+ new ProxySession<>(this, jcrSession, mountSession, this.mountPoints);
+ }
+
+ @Override
+ public void shutdown() {
+
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitSession.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitSession.java
new file mode 100644
index 0000000..97ee586
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitSession.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.jcr.AccessDeniedException;
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Workspace;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.JackrabbitWorkspace;
+import org.apache.jackrabbit.api.security.principal.PrincipalManager;
+import org.apache.jackrabbit.api.security.user.UserManager;
+
+public class ProxyJackrabbitSession extends ProxySession<JackrabbitSession> implements JackrabbitSession {
+ public ProxyJackrabbitSession(ProxyRepository repository, JackrabbitSession jcr, Session mount, Set<String> mountPoints) {
+ super(repository, jcr, mount, mountPoints);
+ }
+
+ @Override
+ public Workspace getWorkspace() {
+ return new ProxyJackrabbitWorkspace(this, (JackrabbitWorkspace) this.jcr.getWorkspace(), (JackrabbitWorkspace) this.mount.getWorkspace());
+ }
+
+ public boolean hasPermission(String absPath, String... actions) throws RepositoryException {
+ if (isMount(absPath)) {
+ return ((JackrabbitSession) mount).hasPermission(absPath, actions);
+ }
+ return jcr.hasPermission(absPath, actions);
+ }
+
+ public PrincipalManager getPrincipalManager() throws AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
+ return jcr.getPrincipalManager();
+ }
+
+ public UserManager getUserManager() throws AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
+ return new ProxyUserManager(this, jcr.getUserManager(), ((JackrabbitSession) mount).getUserManager());
+ }
+
+ public Item getItemOrNull(String absPath) throws RepositoryException {
+ if (super.itemExists(absPath)) {
+ return super.getItem(absPath);
+ } else {
+ return null;
+ }
+ }
+
+ public Property getPropertyOrNull(String absPath) throws RepositoryException {
+ if (super.propertyExists(absPath)) {
+ return super.getProperty(absPath);
+ } else {
+ return null;
+ }
+ }
+
+ public Node getNodeOrNull(String absPath) throws RepositoryException {
+ if (super.nodeExists(absPath)) {
+ return super.getNode(absPath);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitWorkspace.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitWorkspace.java
new file mode 100644
index 0000000..03f5206
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyJackrabbitWorkspace.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.api.JackrabbitWorkspace;
+import org.apache.jackrabbit.api.security.authorization.PrivilegeManager;
+import org.xml.sax.InputSource;
+
+public class ProxyJackrabbitWorkspace extends ProxyWorkspace<JackrabbitWorkspace> implements JackrabbitWorkspace {
+ public ProxyJackrabbitWorkspace(ProxySession mountSession, JackrabbitWorkspace delegate, JackrabbitWorkspace delegate2) {
+ super(mountSession, delegate, delegate2);
+ }
+
+ @Override
+ public void createWorkspace(String workspaceName, InputSource workspaceTemplate) throws AccessDeniedException, RepositoryException {
+ this.delegate.createWorkspace(workspaceName, workspaceTemplate);
+ }
+
+ @Override
+ public PrivilegeManager getPrivilegeManager() throws RepositoryException {
+ return new ProxyPrivilegeManager(mountSession, this.delegate.getPrivilegeManager(), this.delegate2.getPrivilegeManager());
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyLock.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyLock.java
new file mode 100644
index 0000000..20f4316
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyLock.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+
+public class ProxyLock extends ProxyWrapper<Lock> implements Lock {
+ public ProxyLock(ProxySession<?> mountSession, Lock source) {
+ super(mountSession, source);
+ }
+
+ @Override
+ public String getLockOwner() {
+ return delegate.getLockOwner();
+ }
+
+ @Override
+ public boolean isDeep() {
+ return delegate.isDeep();
+ }
+
+ @Override
+ public Node getNode() {
+ return this.mountSession.wrap(this.delegate.getNode());
+ }
+
+ @Override
+ public String getLockToken() {
+ return delegate.getLockToken();
+ }
+
+ @Override
+ public long getSecondsRemaining() throws RepositoryException {
+ return delegate.getSecondsRemaining();
+ }
+
+ @Override
+ public boolean isLive() throws RepositoryException {
+ return delegate.isLive();
+ }
+
+ @Override
+ public boolean isSessionScoped() {
+ return delegate.isSessionScoped();
+ }
+
+ @Override
+ public boolean isLockOwningSession() {
+ return delegate.isLockOwningSession();
+ }
+
+ @Override
+ public void refresh() throws LockException, RepositoryException {
+ delegate.refresh();
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyNamespaceRegistry.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyNamespaceRegistry.java
new file mode 100644
index 0000000..7f70e2f
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyNamespaceRegistry.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.NamespaceException;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+
+public class ProxyNamespaceRegistry implements NamespaceRegistry {
+ private final NamespaceRegistry jcr;
+ private final NamespaceRegistry mount;
+
+ public ProxyNamespaceRegistry(NamespaceRegistry jcr, NamespaceRegistry mount) {
+ this.jcr = jcr;
+ this.mount = mount;
+ }
+
+ @Override
+ public void registerNamespace(String prefix, String uri) throws NamespaceException, UnsupportedRepositoryOperationException, AccessDeniedException, RepositoryException {
+ jcr.registerNamespace(prefix, uri);
+ mount.registerNamespace(prefix, uri);
+ }
+
+ @Override
+ public void unregisterNamespace(String prefix) throws NamespaceException, UnsupportedRepositoryOperationException, AccessDeniedException, RepositoryException {
+ jcr.unregisterNamespace(prefix);
+ mount.unregisterNamespace(prefix);
+ }
+
+ @Override
+ public String[] getPrefixes() throws RepositoryException {
+ return jcr.getPrefixes();
+ }
+
+ @Override
+ public String[] getURIs() throws RepositoryException {
+ return jcr.getURIs();
+ }
+
+ @Override
+ public String getURI(String prefix) throws NamespaceException, RepositoryException {
+ return jcr.getURI(prefix);
+ }
+
+ @Override
+ public String getPrefix(String uri) throws NamespaceException, RepositoryException {
+ return jcr.getPrefix(uri);
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyNode.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyNode.java
new file mode 100644
index 0000000..80a3b2c
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyNode.java
@@ -0,0 +1,413 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.Calendar;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.Binary;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.InvalidLifecycleTransitionException;
+import javax.jcr.Item;
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.MergeException;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.nodetype.NodeDefinition;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.version.ActivityViolationException;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionException;
+import javax.jcr.version.VersionHistory;
+
+public class ProxyNode extends ProxyItem<Node> implements Node {
+ public ProxyNode(ProxySession mountSession, Node node) {
+ super(mountSession, node);
+ }
+
+ @Override
+ public Node addNode(String relPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException {
+ return mountSession.addNode(getPath(), concat(getPath(), relPath), relPath);
+ }
+
+ @Override
+ public Node addNode(String relPath, String primaryNodeTypeName) throws ItemExistsException, PathNotFoundException, NoSuchNodeTypeException, LockException, VersionException, ConstraintViolationException, RepositoryException {
+ return mountSession.addNode(getPath(), concat(getPath(), relPath), relPath, primaryNodeTypeName);
+ }
+
+ @Override
+ public void orderBefore(String srcChildRelPath, String destChildRelPath) throws UnsupportedRepositoryOperationException, VersionException, ConstraintViolationException, ItemNotFoundException, LockException, RepositoryException {
+ this.delegate.orderBefore(srcChildRelPath, destChildRelPath);
+ }
+
+ @Override
+ public Property setProperty(String name, Value value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, value));
+ }
+
+ @Override
+ public Property setProperty(String name, Value value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, value, type));
+ }
+
+ @Override
+ public Property setProperty(String name, Value[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, values));
+ }
+
+ @Override
+ public Property setProperty(String name, Value[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, values, type));
+ }
+
+ @Override
+ public Property setProperty(String name, String[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, values));
+ }
+
+ @Override
+ public Property setProperty(String name, String[] values, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, values, type));
+ }
+
+ @Override
+ public Property setProperty(String name, String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, value));
+ }
+
+ @Override
+ public Property setProperty(String name, String value, int type) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, value, type));
+ }
+
+ @Override
+ public Property setProperty(String name, InputStream value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, value));
+ }
+
+ @Override
+ public Property setProperty(String name, Binary value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, value));
+ }
+
+ @Override
+ public Property setProperty(String name, boolean value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, value));
+ }
+
+ @Override
+ public Property setProperty(String name, double value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, value));
+ }
+
+ @Override
+ public Property setProperty(String name, BigDecimal value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, value));
+ }
+
+ @Override
+ public Property setProperty(String name, long value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, value));
+ }
+
+ @Override
+ public Property setProperty(String name, Calendar value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, value));
+ }
+
+ @Override
+ public Property setProperty(String name, Node value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.setProperty(name, value));
+ }
+
+ @Override
+ public Node getNode(String relPath) throws PathNotFoundException, RepositoryException {
+ return this.mountSession.getNode(concat(getPath(), relPath));
+ }
+
+ @Override
+ public NodeIterator getNodes() throws RepositoryException {
+ return this.mountSession.getNodes(getPath(), this.delegate.getNodes());
+ }
+
+ @Override
+ public NodeIterator getNodes(String namePattern) throws RepositoryException {
+ return this.mountSession.getNodes(getPath(), this.delegate.getNodes(namePattern));
+ }
+
+ @Override
+ public NodeIterator getNodes(String[] nameGlobs) throws RepositoryException {
+ return this.mountSession.getNodes(getPath(), this.delegate.getNodes(nameGlobs));
+ }
+
+ @Override
+ public Property getProperty(String relPath) throws PathNotFoundException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.getProperty(relPath));
+ }
+
+ @Override
+ public PropertyIterator getProperties() throws RepositoryException {
+ return this.mountSession.wrap(this.delegate.getProperties());
+ }
+
+ @Override
+ public PropertyIterator getProperties(String namePattern) throws RepositoryException {
+ return this.mountSession.wrap(this.delegate.getProperties(namePattern));
+ }
+
+ @Override
+ public PropertyIterator getProperties(String[] nameGlobs) throws RepositoryException {
+ return this.mountSession.wrap(this.delegate.getProperties(nameGlobs));
+ }
+
+ @Override
+ public Item getPrimaryItem() throws ItemNotFoundException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.getPrimaryItem());
+ }
+
+ @Override
+ public String getUUID() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return this.delegate.getUUID();
+ }
+
+ @Override
+ public String getIdentifier() throws RepositoryException {
+ return this.delegate.getIdentifier();
+ }
+
+ @Override
+ public int getIndex() throws RepositoryException {
+ return this.delegate.getIndex();
+ }
+
+ @Override
+ public PropertyIterator getReferences() throws RepositoryException {
+ return this.mountSession.wrap(this.delegate.getReferences());
+ }
+
+ @Override
+ public PropertyIterator getReferences(String name) throws RepositoryException {
+ return this.mountSession.wrap(this.delegate.getReferences(name));
+ }
+
+ @Override
+ public PropertyIterator getWeakReferences() throws RepositoryException {
+ return this.mountSession.wrap(this.delegate.getWeakReferences());
+ }
+
+ @Override
+ public PropertyIterator getWeakReferences(String name) throws RepositoryException {
+ return this.mountSession.wrap(this.delegate.getWeakReferences(name));
+ }
+
+ @Override
+ public boolean hasNode(String relPath) throws RepositoryException {
+ return this.mountSession.nodeExists(concat(getPath(), relPath));
+ }
+
+ @Override
+ public boolean hasProperty(String relPath) throws RepositoryException {
+ return this.mountSession.propertyExists(concat(getPath(), relPath));
+ }
+
+ @Override
+ public boolean hasNodes() throws RepositoryException {
+ return this.mountSession.hasNodes(this.delegate);
+ }
+
+ @Override
+ public boolean hasProperties() throws RepositoryException {
+ return this.delegate.hasProperties();
+ }
+
+ @Override
+ public NodeType getPrimaryNodeType() throws RepositoryException {
+ return this.delegate.getPrimaryNodeType();
+ }
+
+ @Override
+ public NodeType[] getMixinNodeTypes() throws RepositoryException {
+ return this.delegate.getMixinNodeTypes();
+ }
+
+ @Override
+ public boolean isNodeType(String nodeTypeName) throws RepositoryException {
+ return this.delegate.isNodeType(nodeTypeName);
+ }
+
+ @Override
+ public void setPrimaryType(String nodeTypeName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
+ this.delegate.setPrimaryType(nodeTypeName);
+ }
+
+ @Override
+ public void addMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
+ this.delegate.addMixin(mixinName);
+ }
+
+ @Override
+ public void removeMixin(String mixinName) throws NoSuchNodeTypeException, VersionException, ConstraintViolationException, LockException, RepositoryException {
+ this.delegate.removeMixin(mixinName);
+ }
+
+ @Override
+ public boolean canAddMixin(String mixinName) throws NoSuchNodeTypeException, RepositoryException {
+ return this.delegate.canAddMixin(mixinName);
+ }
+
+ @Override
+ public NodeDefinition getDefinition() throws RepositoryException {
+ return this.delegate.getDefinition();
+ }
+
+ @Override
+ public Version checkin() throws VersionException, UnsupportedRepositoryOperationException, InvalidItemStateException, LockException, RepositoryException {
+ return this.delegate.checkin();
+ }
+
+ @Override
+ public void checkout() throws UnsupportedRepositoryOperationException, LockException, ActivityViolationException, RepositoryException {
+ this.delegate.checkout();
+ }
+
+ @Override
+ public void doneMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException {
+ this.delegate.doneMerge(version);
+ }
+
+ @Override
+ public void cancelMerge(Version version) throws VersionException, InvalidItemStateException, UnsupportedRepositoryOperationException, RepositoryException {
+ this.delegate.cancelMerge(version);
+ }
+
+ @Override
+ public void update(String srcWorkspace) throws NoSuchWorkspaceException, AccessDeniedException, LockException, InvalidItemStateException, RepositoryException {
+ this.delegate.update(srcWorkspace);
+ }
+
+ @Override
+ public NodeIterator merge(String srcWorkspace, boolean bestEffort) throws NoSuchWorkspaceException, AccessDeniedException, MergeException, LockException, InvalidItemStateException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.merge(srcWorkspace, bestEffort));
+ }
+
+ @Override
+ public String getCorrespondingNodePath(String workspaceName) throws ItemNotFoundException, NoSuchWorkspaceException, AccessDeniedException, RepositoryException {
+ return this.delegate.getCorrespondingNodePath(workspaceName);
+ }
+
+ @Override
+ public NodeIterator getSharedSet() throws RepositoryException {
+ return this.mountSession.wrap(this.delegate.getSharedSet());
+ }
+
+ @Override
+ public void removeSharedSet() throws VersionException, LockException, ConstraintViolationException, RepositoryException {
+ this.delegate.removeSharedSet();
+ }
+
+ @Override
+ public void removeShare() throws VersionException, LockException, ConstraintViolationException, RepositoryException {
+ this.delegate.removeShare();
+ }
+
+ @Override
+ public boolean isCheckedOut() throws RepositoryException {
+ return this.delegate.isCheckedOut();
+ }
+
+ @Override
+ public void restore(String versionName, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
+ this.delegate.restore(versionName, removeExisting);
+ }
+
+ @Override
+ public void restore(Version version, boolean removeExisting) throws VersionException, ItemExistsException, InvalidItemStateException, UnsupportedRepositoryOperationException, LockException, RepositoryException {
+ this.delegate.restore(version, removeExisting);
+ }
+
+ @Override
+ public void restore(Version version, String relPath, boolean removeExisting) throws PathNotFoundException, ItemExistsException, VersionException, ConstraintViolationException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
+ this.delegate.restore(version, relPath, removeExisting);
+ }
+
+ @Override
+ public void restoreByLabel(String versionLabel, boolean removeExisting) throws VersionException, ItemExistsException, UnsupportedRepositoryOperationException, LockException, InvalidItemStateException, RepositoryException {
+ this.delegate.restoreByLabel(versionLabel, removeExisting);
+ }
+
+ @Override
+ public VersionHistory getVersionHistory() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return this.delegate.getVersionHistory();
+ }
+
+ @Override
+ public Version getBaseVersion() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return this.delegate.getBaseVersion();
+ }
+
+ @Override
+ public Lock lock(boolean isDeep, boolean isSessionScoped) throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.lock(isDeep, isSessionScoped));
+ }
+
+ @Override
+ public Lock getLock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, RepositoryException {
+ return this.mountSession.wrap(this.delegate.getLock());
+ }
+
+ @Override
+ public void unlock() throws UnsupportedRepositoryOperationException, LockException, AccessDeniedException, InvalidItemStateException, RepositoryException {
+ this.delegate.unlock();
+ }
+
+ @Override
+ public boolean holdsLock() throws RepositoryException {
+ return this.delegate.holdsLock();
+ }
+
+ @Override
+ public boolean isLocked() throws RepositoryException {
+ return this.delegate.isLocked();
+ }
+
+ @Override
+ public void followLifecycleTransition(String transition) throws UnsupportedRepositoryOperationException, InvalidLifecycleTransitionException, RepositoryException {
+ this.delegate.followLifecycleTransition(transition);
+ }
+
+ @Override
+ public String[] getAllowedLifecycleTransistions() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return this.delegate.getAllowedLifecycleTransistions();
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyNodeTypeManager.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyNodeTypeManager.java
new file mode 100644
index 0000000..29d378e
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyNodeTypeManager.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.nodetype.InvalidNodeTypeDefinitionException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.nodetype.NodeDefinitionTemplate;
+import javax.jcr.nodetype.NodeType;
+import javax.jcr.nodetype.NodeTypeDefinition;
+import javax.jcr.nodetype.NodeTypeExistsException;
+import javax.jcr.nodetype.NodeTypeIterator;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.nodetype.NodeTypeTemplate;
+import javax.jcr.nodetype.PropertyDefinitionTemplate;
+
+public class ProxyNodeTypeManager implements NodeTypeManager {
+ private final NodeTypeManager nodeTypeManager;
+ private final NodeTypeManager nodeTypeManager1;
+
+ public ProxyNodeTypeManager(NodeTypeManager nodeTypeManager, NodeTypeManager nodeTypeManager1) {
+ this.nodeTypeManager = nodeTypeManager;
+ this.nodeTypeManager1 = nodeTypeManager1;
+ }
+
+ @Override
+ public NodeType getNodeType(String nodeTypeName) throws NoSuchNodeTypeException, RepositoryException {
+ return nodeTypeManager.getNodeType(nodeTypeName);
+ }
+
+ @Override
+ public boolean hasNodeType(String name) throws RepositoryException {
+ return nodeTypeManager.hasNodeType(name);
+ }
+
+ @Override
+ public NodeTypeIterator getAllNodeTypes() throws RepositoryException {
+ return nodeTypeManager.getAllNodeTypes();
+ }
+
+ @Override
+ public NodeTypeIterator getPrimaryNodeTypes() throws RepositoryException {
+ return nodeTypeManager.getPrimaryNodeTypes();
+ }
+
+ @Override
+ public NodeTypeIterator getMixinNodeTypes() throws RepositoryException {
+ return nodeTypeManager.getMixinNodeTypes();
+ }
+
+ @Override
+ public NodeTypeTemplate createNodeTypeTemplate() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return nodeTypeManager.createNodeTypeTemplate();
+ }
+
+ @Override
+ public NodeTypeTemplate createNodeTypeTemplate(NodeTypeDefinition ntd) throws UnsupportedRepositoryOperationException, RepositoryException {
+ return nodeTypeManager.createNodeTypeTemplate(ntd);
+ }
+
+ @Override
+ public NodeDefinitionTemplate createNodeDefinitionTemplate() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return nodeTypeManager.createNodeDefinitionTemplate();
+ }
+
+ @Override
+ public PropertyDefinitionTemplate createPropertyDefinitionTemplate() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return nodeTypeManager.createPropertyDefinitionTemplate();
+ }
+
+ @Override
+ public NodeType registerNodeType(NodeTypeDefinition ntd, boolean allowUpdate) throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, UnsupportedRepositoryOperationException, RepositoryException {
+ nodeTypeManager1.registerNodeType(ntd, allowUpdate);
+ return nodeTypeManager.registerNodeType(ntd, allowUpdate);
+ }
+
+ @Override
+ public NodeTypeIterator registerNodeTypes(NodeTypeDefinition[] ntds, boolean allowUpdate) throws InvalidNodeTypeDefinitionException, NodeTypeExistsException, UnsupportedRepositoryOperationException, RepositoryException {
+ nodeTypeManager1.registerNodeTypes(ntds, allowUpdate);
+ return nodeTypeManager.registerNodeTypes(ntds, allowUpdate);
+ }
+
+ @Override
+ public void unregisterNodeType(String name) throws UnsupportedRepositoryOperationException, NoSuchNodeTypeException, RepositoryException {
+ nodeTypeManager.unregisterNodeType(name);
+ nodeTypeManager1.unregisterNodeType(name);
+ }
+
+ @Override
+ public void unregisterNodeTypes(String[] names) throws UnsupportedRepositoryOperationException, NoSuchNodeTypeException, RepositoryException {
+ nodeTypeManager.unregisterNodeTypes(names);
+ nodeTypeManager1.unregisterNodeTypes(names);
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyPrivilegeManager.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyPrivilegeManager.java
new file mode 100644
index 0000000..ae4eda6
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyPrivilegeManager.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.NamespaceException;
+import javax.jcr.RepositoryException;
+import javax.jcr.security.AccessControlException;
+import javax.jcr.security.Privilege;
+
+import org.apache.jackrabbit.api.security.authorization.PrivilegeManager;
+
+public class ProxyPrivilegeManager extends ProxyWrapper<PrivilegeManager> implements PrivilegeManager {
+ private final PrivilegeManager mount;
+
+ public ProxyPrivilegeManager(ProxySession<?> mountSession, PrivilegeManager delegate, PrivilegeManager mount) {
+ super(mountSession, delegate);
+ this.mount = mount;
+ }
+
+ public Privilege[] getRegisteredPrivileges() throws RepositoryException {
+ List<Privilege> result = new ArrayList<>();
+ result.addAll(Arrays.asList(delegate.getRegisteredPrivileges()));
+
+ result.addAll(Arrays.asList(mount.getRegisteredPrivileges()));
+
+ return result.toArray(new Privilege[0]);
+ }
+
+ public Privilege getPrivilege(String privilegeName) throws AccessControlException, RepositoryException {
+ try {
+ return mount.getPrivilege(privilegeName);
+ } catch (AccessControlException ex) {
+ return delegate.getPrivilege(privilegeName);
+ }
+ }
+
+ public Privilege registerPrivilege(String privilegeName, boolean isAbstract, String[] declaredAggregateNames) throws AccessDeniedException, NamespaceException, RepositoryException {
+ mount.registerPrivilege(privilegeName, isAbstract, declaredAggregateNames);
+ return delegate.registerPrivilege(privilegeName, isAbstract, declaredAggregateNames);
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyProperty.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyProperty.java
new file mode 100644
index 0000000..50cd8f9
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyProperty.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import java.io.InputStream;
+import java.math.BigDecimal;
+import java.util.Calendar;
+
+import javax.jcr.Binary;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.PropertyDefinition;
+import javax.jcr.version.VersionException;
+
+public class ProxyProperty extends ProxyItem<Property> implements Property {
+ public ProxyProperty(ProxySession mountSession, Property delegate) {
+ super(mountSession, delegate);
+ }
+
+ public void setValue(Value value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ delegate.setValue(value);
+ }
+
+ public void setValue(Value[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ delegate.setValue(values);
+ }
+
+ public void setValue(String value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ delegate.setValue(value);
+ }
+
+ public void setValue(String[] values) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ delegate.setValue(values);
+ }
+
+ public void setValue(InputStream value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ delegate.setValue(value);
+ }
+
+ public void setValue(Binary value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ delegate.setValue(value);
+ }
+
+ public void setValue(long value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ delegate.setValue(value);
+ }
+
+ public void setValue(double value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ delegate.setValue(value);
+ }
+
+ public void setValue(BigDecimal value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ delegate.setValue(value);
+ }
+
+ public void setValue(Calendar value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ delegate.setValue(value);
+ }
+
+ public void setValue(boolean value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ delegate.setValue(value);
+ }
+
+ public void setValue(Node value) throws ValueFormatException, VersionException, LockException, ConstraintViolationException, RepositoryException {
+ delegate.setValue(value);
+ }
+
+ public Value getValue() throws ValueFormatException, RepositoryException {
+ return delegate.getValue();
+ }
+
+ public Value[] getValues() throws ValueFormatException, RepositoryException {
+ return delegate.getValues();
+ }
+
+ public String getString() throws ValueFormatException, RepositoryException {
+ return delegate.getString();
+ }
+
+ public InputStream getStream() throws ValueFormatException, RepositoryException {
+ return delegate.getStream();
+ }
+
+ public Binary getBinary() throws ValueFormatException, RepositoryException {
+ return delegate.getBinary();
+ }
+
+ public long getLong() throws ValueFormatException, RepositoryException {
+ return delegate.getLong();
+ }
+
+ public double getDouble() throws ValueFormatException, RepositoryException {
+ return delegate.getDouble();
+ }
+
+ public BigDecimal getDecimal() throws ValueFormatException, RepositoryException {
+ return delegate.getDecimal();
+ }
+
+ public Calendar getDate() throws ValueFormatException, RepositoryException {
+ return delegate.getDate();
+ }
+
+ public boolean getBoolean() throws ValueFormatException, RepositoryException {
+ return delegate.getBoolean();
+ }
+
+ public Node getNode() throws ItemNotFoundException, ValueFormatException, RepositoryException {
+ return this.mountSession.getNode(delegate.getNode().getPath());
+ }
+
+ public Property getProperty() throws ItemNotFoundException, ValueFormatException, RepositoryException {
+ return this.mountSession.getProperty(delegate.getProperty().getPath());
+ }
+
+ public long getLength() throws ValueFormatException, RepositoryException {
+ return delegate.getLength();
+ }
+
+ public long[] getLengths() throws ValueFormatException, RepositoryException {
+ return delegate.getLengths();
+ }
+
+ public PropertyDefinition getDefinition() throws RepositoryException {
+ return delegate.getDefinition();
+ }
+
+ public int getType() throws RepositoryException {
+ return delegate.getType();
+ }
+
+ public boolean isMultiple() throws RepositoryException {
+ return delegate.isMultiple();
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQuery.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQuery.java
new file mode 100644
index 0000000..770f7a5
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQuery.java
@@ -0,0 +1,233 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Value;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.Row;
+import javax.jcr.query.RowIterator;
+import javax.jcr.version.VersionException;
+
+public class ProxyQuery extends ProxyWrapper<Query> implements Query {
+ private final Query delegate2;
+
+ public ProxyQuery(ProxySession<?> mountSession, Query delegate, Query delegate2) {
+ super(mountSession, delegate);
+ this.delegate2 = delegate2;
+ }
+
+ public QueryResult execute() throws InvalidQueryException, RepositoryException {
+ final QueryResult result1 = delegate.execute();
+ QueryResult result2temp = null;
+ if (delegate2 != null) {
+ result2temp = delegate2.execute();
+ }
+ final QueryResult result2 = result2temp;
+
+ return this.mountSession.wrap(new QueryResult() {
+ @Override
+ public String[] getColumnNames() throws RepositoryException {
+ return result1.getColumnNames();
+ }
+
+ @Override
+ public RowIterator getRows() throws RepositoryException {
+ final RowIterator i1 = result1.getRows();
+ RowIterator i2 = null;
+ if (result2 != null) {
+ i2 = result2.getRows();
+ }
+ if ( i2 == null || !i2.hasNext() ) {
+ return i1;
+ }
+ if ( !i1.hasNext() ) {
+ return i2;
+ }
+ final List<RowIterator> list = new ArrayList<>();
+ list.add(i1);
+ list.add(i2);
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ final Iterator<Row> iter = new ChainedIterator(list.iterator());
+ return new RowIterator() {
+
+ @Override
+ public Object next() {
+ return iter.next();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ @Override
+ public void skip(long skipNum) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public long getSize() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public long getPosition() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public Row nextRow() {
+ return iter.next();
+ }
+ };
+ }
+
+ @Override
+ public NodeIterator getNodes() throws RepositoryException {
+ final NodeIterator i1 = result1.getNodes();
+ NodeIterator i2 = null;
+ if (result2 != null) {
+ i2 = result2.getNodes();
+ }
+ if ( i2 == null || !i2.hasNext() ) {
+ return i1;
+ }
+ if ( !i1.hasNext() ) {
+ return i2;
+ }
+ final List<NodeIterator> list = new ArrayList<>();
+ list.add(i1);
+ list.add(i2);
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ final Iterator<Node> iter = new ChainedIterator(list.iterator());
+ return new NodeIterator() {
+
+ @Override
+ public Object next() {
+ return iter.next();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ @Override
+ public void skip(long skipNum) {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public long getSize() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public long getPosition() {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public Node nextNode() {
+ return iter.next();
+ }
+ };
+ }
+
+ @Override
+ public String[] getSelectorNames() throws RepositoryException {
+ return result1.getSelectorNames();
+ }
+ });
+ }
+
+ public void setLimit(long limit) {
+ delegate.setLimit(limit);
+ if (delegate2 != null) {
+ delegate2.setLimit(limit);
+ }
+ }
+
+ public void setOffset(long offset) {
+ delegate.setOffset(offset);
+ if (delegate2 != null) {
+ delegate2.setOffset(2);
+ }
+ }
+
+ public String getStatement() {
+ return delegate.getStatement();
+ }
+
+ public String getLanguage() {
+ return delegate.getLanguage();
+ }
+
+ public String getStoredQueryPath() throws ItemNotFoundException, RepositoryException {
+ try {
+ return delegate.getStoredQueryPath();
+ } catch (ItemNotFoundException ex) {
+ try {
+ if (delegate2 != null) {
+ return delegate2.getStoredQueryPath();
+ } else {
+ return "";
+ }
+ } catch (ItemNotFoundException ignore) {
+ throw ex;
+ }
+ }
+ }
+
+ public Node storeAsNode(String absPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, UnsupportedRepositoryOperationException, RepositoryException {
+ return this.mountSession.wrap(this.mountSession.isMount(absPath) ? delegate2.storeAsNode(absPath) : delegate.storeAsNode(absPath));
+ }
+
+ public void bindValue(String varName, Value value) throws IllegalArgumentException, RepositoryException {
+ delegate.bindValue(varName, value);
+ if (delegate2 != null) {
+ delegate2.bindValue(varName, value);
+ }
+ }
+
+ public String[] getBindVariableNames() throws RepositoryException {
+ return delegate.getBindVariableNames();
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQueryManager.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQueryManager.java
new file mode 100644
index 0000000..ed897ae
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQueryManager.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.Query;
+import javax.jcr.query.QueryManager;
+import javax.jcr.query.qom.QueryObjectModelFactory;
+
+public class ProxyQueryManager extends ProxyWrapper<QueryManager> implements QueryManager {
+ private final QueryManager delegate2;
+
+ public ProxyQueryManager(ProxySession<?> mountSession, QueryManager delegate, QueryManager delegate2) {
+ super(mountSession, delegate);
+ this.delegate2 = delegate2;
+ }
+
+ @Override
+ public Query createQuery(String statement, String language) throws InvalidQueryException, RepositoryException {
+ return new ProxyQuery(this.mountSession, delegate.createQuery(statement, language), delegate2.createQuery(statement, language));
+ }
+
+ @Override
+ public QueryObjectModelFactory getQOMFactory() {
+ return new ProxyQueryObjectModelFactory(this.mountSession, delegate.getQOMFactory(), delegate2.getQOMFactory());
+ }
+
+ @Override
+ public Query getQuery(Node node) throws InvalidQueryException, RepositoryException {
+ return this.mountSession.wrap(delegate.getQuery(this.mountSession.unwrap(node)));
+ }
+
+ @Override
+ public String[] getSupportedQueryLanguages() throws RepositoryException {
+ return delegate.getSupportedQueryLanguages();
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQueryObjectModel.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQueryObjectModel.java
new file mode 100644
index 0000000..4cc6ecc
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQueryObjectModel.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import javax.jcr.query.qom.Column;
+import javax.jcr.query.qom.Constraint;
+import javax.jcr.query.qom.Ordering;
+import javax.jcr.query.qom.QueryObjectModel;
+import javax.jcr.query.qom.Source;
+
+public class ProxyQueryObjectModel extends ProxyQuery implements QueryObjectModel {
+ public ProxyQueryObjectModel(ProxySession<?> mountSession, QueryObjectModel delegate, QueryObjectModel delegate2) {
+ super(mountSession, delegate, delegate2);
+ }
+
+ public Source getSource() {
+ return ((QueryObjectModel) delegate).getSource();
+ }
+
+ public Constraint getConstraint() {
+ return ((QueryObjectModel) delegate).getConstraint();
+ }
+
+ public Ordering[] getOrderings() {
+ return ((QueryObjectModel) delegate).getOrderings();
+ }
+
+ public Column[] getColumns() {
+ return ((QueryObjectModel) delegate).getColumns();
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQueryObjectModelFactory.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQueryObjectModelFactory.java
new file mode 100644
index 0000000..35d2aa0
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQueryObjectModelFactory.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.query.InvalidQueryException;
+import javax.jcr.query.qom.And;
+import javax.jcr.query.qom.BindVariableValue;
+import javax.jcr.query.qom.ChildNode;
+import javax.jcr.query.qom.ChildNodeJoinCondition;
+import javax.jcr.query.qom.Column;
+import javax.jcr.query.qom.Comparison;
+import javax.jcr.query.qom.Constraint;
+import javax.jcr.query.qom.DescendantNode;
+import javax.jcr.query.qom.DescendantNodeJoinCondition;
+import javax.jcr.query.qom.DynamicOperand;
+import javax.jcr.query.qom.EquiJoinCondition;
+import javax.jcr.query.qom.FullTextSearch;
+import javax.jcr.query.qom.FullTextSearchScore;
+import javax.jcr.query.qom.Join;
+import javax.jcr.query.qom.JoinCondition;
+import javax.jcr.query.qom.Length;
+import javax.jcr.query.qom.Literal;
+import javax.jcr.query.qom.LowerCase;
+import javax.jcr.query.qom.NodeLocalName;
+import javax.jcr.query.qom.NodeName;
+import javax.jcr.query.qom.Not;
+import javax.jcr.query.qom.Or;
+import javax.jcr.query.qom.Ordering;
+import javax.jcr.query.qom.PropertyExistence;
+import javax.jcr.query.qom.PropertyValue;
+import javax.jcr.query.qom.QueryObjectModel;
+import javax.jcr.query.qom.QueryObjectModelFactory;
+import javax.jcr.query.qom.SameNode;
+import javax.jcr.query.qom.SameNodeJoinCondition;
+import javax.jcr.query.qom.Selector;
+import javax.jcr.query.qom.Source;
+import javax.jcr.query.qom.StaticOperand;
+import javax.jcr.query.qom.UpperCase;
+
+public class ProxyQueryObjectModelFactory extends ProxyWrapper<QueryObjectModelFactory> implements QueryObjectModelFactory {
+ private final QueryObjectModelFactory delegate2;
+
+ public ProxyQueryObjectModelFactory(ProxySession<?> mountSession, QueryObjectModelFactory delegate, QueryObjectModelFactory delegate2) {
+ super(mountSession, delegate);
+ this.delegate2 = delegate2;
+ }
+
+ public QueryObjectModel createQuery(Source source, Constraint constraint, Ordering[] orderings, Column[] columns) throws InvalidQueryException, RepositoryException {
+ if (delegate2 != null) {
+ return new ProxyQueryObjectModel(this.mountSession, delegate.createQuery(source, constraint, orderings, columns),
+ delegate2.createQuery(source, constraint, orderings, columns));
+ } else {
+ return new ProxyQueryObjectModel(this.mountSession, delegate.createQuery(source, constraint, orderings, columns),
+ null);
+ }
+ }
+
+ public Selector selector(String nodeTypeName, String selectorName) throws InvalidQueryException, RepositoryException {
+ return delegate.selector(nodeTypeName, selectorName);
+ }
+
+ public Join join(Source left, Source right, String joinType, JoinCondition joinCondition) throws InvalidQueryException, RepositoryException {
+ return delegate.join(left, right, joinType, joinCondition);
+ }
+
+ public EquiJoinCondition equiJoinCondition(String selector1Name, String property1Name, String selector2Name, String property2Name) throws InvalidQueryException, RepositoryException {
+ return delegate.equiJoinCondition(selector1Name, property1Name, selector2Name, property2Name);
+ }
+
+ public SameNodeJoinCondition sameNodeJoinCondition(String selector1Name, String selector2Name, String selector2Path) throws InvalidQueryException, RepositoryException {
+ return delegate.sameNodeJoinCondition(selector1Name, selector2Name, selector2Path);
+ }
+
+ public ChildNodeJoinCondition childNodeJoinCondition(String childSelectorName, String parentSelectorName) throws InvalidQueryException, RepositoryException {
+ return delegate.childNodeJoinCondition(childSelectorName, parentSelectorName);
+ }
+
+ public DescendantNodeJoinCondition descendantNodeJoinCondition(String descendantSelectorName, String ancestorSelectorName) throws InvalidQueryException, RepositoryException {
+ return delegate.descendantNodeJoinCondition(descendantSelectorName, ancestorSelectorName);
+ }
+
+ public And and(Constraint constraint1, Constraint constraint2) throws InvalidQueryException, RepositoryException {
+ return delegate.and(constraint1, constraint2);
+ }
+
+ public Or or(Constraint constraint1, Constraint constraint2) throws InvalidQueryException, RepositoryException {
+ return delegate.or(constraint1, constraint2);
+ }
+
+ public Not not(Constraint constraint) throws InvalidQueryException, RepositoryException {
+ return delegate.not(constraint);
+ }
+
+ public Comparison comparison(DynamicOperand operand1, String operator, StaticOperand operand2) throws InvalidQueryException, RepositoryException {
+ return delegate.comparison(operand1, operator, operand2);
+ }
+
+ public PropertyExistence propertyExistence(String selectorName, String propertyName) throws InvalidQueryException, RepositoryException {
+ return delegate.propertyExistence(selectorName, propertyName);
+ }
+
+ public FullTextSearch fullTextSearch(String selectorName, String propertyName, StaticOperand fullTextSearchExpression) throws InvalidQueryException, RepositoryException {
+ return delegate.fullTextSearch(selectorName, propertyName, fullTextSearchExpression);
+ }
+
+ public SameNode sameNode(String selectorName, String path) throws InvalidQueryException, RepositoryException {
+ return delegate.sameNode(selectorName, path);
+ }
+
+ public ChildNode childNode(String selectorName, String path) throws InvalidQueryException, RepositoryException {
+ return delegate.childNode(selectorName, path);
+ }
+
+ public DescendantNode descendantNode(String selectorName, String path) throws InvalidQueryException, RepositoryException {
+ return delegate.descendantNode(selectorName, path);
+ }
+
+ public PropertyValue propertyValue(String selectorName, String propertyName) throws InvalidQueryException, RepositoryException {
+ return delegate.propertyValue(selectorName, propertyName);
+ }
+
+ public Length length(PropertyValue propertyValue) throws InvalidQueryException, RepositoryException {
+ return delegate.length(propertyValue);
+ }
+
+ public NodeName nodeName(String selectorName) throws InvalidQueryException, RepositoryException {
+ return delegate.nodeName(selectorName);
+ }
+
+ public NodeLocalName nodeLocalName(String selectorName) throws InvalidQueryException, RepositoryException {
+ return delegate.nodeLocalName(selectorName);
+ }
+
+ public FullTextSearchScore fullTextSearchScore(String selectorName) throws InvalidQueryException, RepositoryException {
+ return delegate.fullTextSearchScore(selectorName);
+ }
+
+ public LowerCase lowerCase(DynamicOperand operand) throws InvalidQueryException, RepositoryException {
+ return delegate.lowerCase(operand);
+ }
+
+ public UpperCase upperCase(DynamicOperand operand) throws InvalidQueryException, RepositoryException {
+ return delegate.upperCase(operand);
+ }
+
+ public BindVariableValue bindVariable(String bindVariableName) throws InvalidQueryException, RepositoryException {
+ return delegate.bindVariable(bindVariableName);
+ }
+
+ public Literal literal(Value literalValue) throws InvalidQueryException, RepositoryException {
+ return delegate.literal(literalValue);
+ }
+
+ public Ordering ascending(DynamicOperand operand) throws InvalidQueryException, RepositoryException {
+ return delegate.ascending(operand);
+ }
+
+ public Ordering descending(DynamicOperand operand) throws InvalidQueryException, RepositoryException {
+ return delegate.descending(operand);
+ }
+
+ public Column column(String selectorName, String propertyName, String columnName) throws InvalidQueryException, RepositoryException {
+ return delegate.column(selectorName, propertyName, columnName);
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQueryResult.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQueryResult.java
new file mode 100644
index 0000000..ee9e1fd
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyQueryResult.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.RowIterator;
+
+public class ProxyQueryResult extends ProxyWrapper<QueryResult> implements QueryResult {
+ public ProxyQueryResult(ProxySession<?> mountSession, QueryResult delegate) {
+ super(mountSession, delegate);
+ }
+
+ public String[] getColumnNames() throws RepositoryException {
+ return delegate.getColumnNames();
+ }
+
+ public RowIterator getRows() throws RepositoryException {
+ return this.mountSession.wrap(delegate.getRows());
+ }
+
+ public NodeIterator getNodes() throws RepositoryException {
+ return this.mountSession.wrap(delegate.getNodes());
+ }
+
+ public String[] getSelectorNames() throws RepositoryException {
+ return delegate.getSelectorNames();
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyRepository.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyRepository.java
new file mode 100644
index 0000000..40b685c
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyRepository.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.api.JackrabbitRepository;
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.sling.jcr.base.spi.RepositoryMount;
+
+public class ProxyRepository<T extends Repository> implements Repository {
+ public final T jcr;
+ final T mount;
+ final Set<String> mountPoints;
+
+ public ProxyRepository(T jcr, T mount, Set<String> mountPoint) {
+ this.jcr = jcr;
+ this.mount = mount;
+ this.mountPoints = new HashSet<>(mountPoint);
+ }
+
+
+ @Override
+ public String[] getDescriptorKeys() {
+ return jcr.getDescriptorKeys();
+ }
+
+ @Override
+ public boolean isStandardDescriptor(String key) {
+ return jcr.isStandardDescriptor(key);
+ }
+
+ @Override
+ public boolean isSingleValueDescriptor(String key) {
+ return jcr.isSingleValueDescriptor(key);
+ }
+
+ @Override
+ public Value getDescriptorValue(String key) {
+ return jcr.getDescriptorValue(key);
+ }
+
+ @Override
+ public Value[] getDescriptorValues(String key) {
+ return jcr.getDescriptorValues(key);
+ }
+
+ @Override
+ public String getDescriptor(String key) {
+ return jcr.getDescriptor(key);
+ }
+
+ @Override
+ public Session login(Credentials credentials, String workspaceName) throws LoginException, NoSuchWorkspaceException, RepositoryException {
+ Session jcrSession = jcr.login(credentials, workspaceName);
+
+ Session mountSession;
+ if (mount instanceof JackrabbitRepository) {
+ Map<String, Object> attributes = new HashMap<>();
+ attributes.put(RepositoryMount.PARENT_SESSION_KEY, jcrSession);
+ mountSession = ((JackrabbitRepository) mount).login(credentials, workspaceName, attributes);
+ }
+ else {
+ mountSession = mount.login(credentials, workspaceName);
+ }
+ return jcrSession instanceof JackrabbitSession ?
+ new ProxyJackrabbitSession(this, (JackrabbitSession) jcrSession, mountSession, this.mountPoints) :
+ new ProxySession<>(this, jcrSession, mountSession, this.mountPoints);
+ }
+
+ @Override
+ public Session login(Credentials credentials) throws LoginException, RepositoryException {
+ return login(credentials, null);
+ }
+
+ @Override
+ public Session login(String workspaceName) throws LoginException, NoSuchWorkspaceException, RepositoryException {
+ return login(null, workspaceName);
+ }
+
+ @Override
+ public Session login() throws LoginException, RepositoryException {
+ return login(null, null);
+ }
+
+ public Session wrap(Session session) throws RepositoryException {
+ if (session instanceof ProxySession) {
+ return session;
+ }
+
+ Map<String, Object> attributes = new HashMap<>();
+ attributes.put(RepositoryMount.PARENT_SESSION_KEY, session);
+ Session mountSession = ((JackrabbitRepository) mount).login(new SimpleCredentials(session.getUserID(), new char[0]),session.getWorkspace().getName(), attributes );
+
+ return session instanceof JackrabbitSession ?
+ new ProxyJackrabbitSession(this, (JackrabbitSession) session, mountSession, this.mountPoints) :
+ new ProxySession<>(this, session, mountSession, this.mountPoints);
+ }
+
+ Session impersonate(Credentials credentials, Session jcr, Session mount) throws RepositoryException {
+ return jcr instanceof JackrabbitSession ?
+ new ProxyJackrabbitSession(this, (JackrabbitSession) jcr.impersonate(credentials), mount.impersonate(credentials), this.mountPoints) :
+ new ProxySession<>(this, jcr.impersonate(credentials), mount.impersonate(credentials), this.mountPoints);
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxySession.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxySession.java
new file mode 100644
index 0000000..efbc475
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxySession.java
@@ -0,0 +1,615 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.AccessControlException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.Credentials;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.InvalidSerializedDataException;
+import javax.jcr.Item;
+import javax.jcr.ItemExistsException;
+import javax.jcr.ItemNotFoundException;
+import javax.jcr.LoginException;
+import javax.jcr.NamespaceException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.ReferentialIntegrityException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Value;
+import javax.jcr.ValueFactory;
+import javax.jcr.Workspace;
+import javax.jcr.lock.Lock;
+import javax.jcr.lock.LockException;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NoSuchNodeTypeException;
+import javax.jcr.query.QueryResult;
+import javax.jcr.query.Row;
+import javax.jcr.query.RowIterator;
+import javax.jcr.retention.RetentionManager;
+import javax.jcr.security.AccessControlManager;
+import javax.jcr.version.VersionException;
+
+import org.apache.jackrabbit.api.security.JackrabbitAccessControlManager;
+import org.apache.jackrabbit.commons.iterator.NodeIteratorAdapter;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+public class ProxySession<T extends Session> implements Session {
+ private final ProxyRepository repository;
+ public final T jcr;
+ protected final Session mount;
+ private final Set<String> mountPoints;
+
+ public ProxySession(ProxyRepository repository, T jcr, Session mount, Set<String> mountPoints) {
+ this.repository = repository;
+ this.jcr = jcr;
+ this.mount = mount;
+ this.mountPoints = mountPoints;
+ }
+
+ boolean isMount(String path) {
+ return path != null && (mountPoints.contains(path) || mountPoints.stream().anyMatch(mountPoint -> path.startsWith(mountPoint + "/")));
+ }
+
+ boolean isMountParent(String path) {
+ return mountPoints.stream().anyMatch(mountPoint -> mountPoint.startsWith((path + "/").replace("//", "/")));
+ }
+
+ boolean isMountDirectParent(String path) {
+ return mountPoints.stream().anyMatch(mountPoint -> PathUtils.getParentPath(mountPoint).equals(path));
+ }
+
+ public <F> F wrap(F source) {
+ if (source instanceof ProxyWrapper) {
+ return source;
+ }
+ return (F) (source instanceof Node ? new ProxyNode(this, (Node) source) :
+ source instanceof Property ? new ProxyProperty(this, (Property) source) :
+ source instanceof Item ? new ProxyItem<>(this, (Item) source) :
+ source instanceof Lock ? new ProxyLock(this, (Lock) source) :
+ source instanceof QueryResult ? new ProxyQueryResult(this, (QueryResult) source) :
+ source);
+ }
+
+ public <F> F unwrap(F source) {
+ return (F) (source instanceof ProxyWrapper ? ((ProxyWrapper) source).delegate : source);
+ }
+
+ public NodeIterator wrap(final NodeIterator iter) {
+ return new NodeIteratorAdapter(new Iterator<Node>() {
+ @Override
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ @Override
+ public Node next() {
+ return wrap(iter.nextNode());
+ }
+
+ @Override
+ public void remove() {
+ iter.remove();
+ }
+ });
+ }
+
+ public PropertyIterator wrap(final PropertyIterator iter) {
+ return new PropertyIterator() {
+ @Override
+ public Property nextProperty() {
+ return wrap(iter.nextProperty());
+ }
+
+ @Override
+ public void skip(long skipNum) {
+ iter.skip(skipNum);
+ }
+
+ @Override
+ public long getSize() {
+ return iter.getSize();
+ }
+
+ @Override
+ public long getPosition() {
+ return iter.getPosition();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ @Override
+ public void remove() {
+ iter.remove();
+ }
+
+ @Override
+ public Object next() {
+ return wrap(iter.next());
+ }
+ };
+ }
+
+ public RowIterator wrap(final RowIterator iter) {
+ return new RowIterator() {
+
+ @Override
+ public Row nextRow() {
+ final Row row = iter.nextRow();
+
+ return new Row() {
+ @Override
+ public Value[] getValues() throws RepositoryException {
+ return row.getValues();
+ }
+
+ @Override
+ public Value getValue(String s) throws ItemNotFoundException, RepositoryException {
+ return row.getValue(s);
+ }
+
+ @Override
+ public Node getNode() throws RepositoryException {
+ return wrap(row.getNode());
+ }
+
+ @Override
+ public Node getNode(String s) throws RepositoryException {
+ return wrap(row.getNode(s));
+ }
+
+ @Override
+ public String getPath() throws RepositoryException {
+ return row.getPath();
+ }
+
+ @Override
+ public String getPath(String s) throws RepositoryException {
+ return row.getPath(s);
+ }
+
+ @Override
+ public double getScore() throws RepositoryException {
+ return row.getScore();
+ }
+
+ @Override
+ public double getScore(String s) throws RepositoryException {
+ return row.getScore(s);
+ }
+ };
+ }
+
+ @Override
+ public void skip(long l) {
+ iter.skip(l);
+ }
+
+ @Override
+ public long getSize() {
+ return iter.getSize();
+ }
+
+ @Override
+ public long getPosition() {
+ return iter.getPosition();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return iter.hasNext();
+ }
+
+ @Override
+ public Object next() {
+ return nextRow();
+ }
+
+ @Override
+ public void remove() {
+ iter.remove();
+ }
+ };
+ }
+
+ @Override
+ public Repository getRepository() {
+ return this.repository;
+ }
+
+ @Override
+ public String getUserID() {
+ return this.jcr.getUserID();
+ }
+
+ @Override
+ public String[] getAttributeNames() {
+ return this.jcr.getAttributeNames();
+ }
+
+ @Override
+ public Object getAttribute(String name) {
+ return this.jcr.getAttribute(name);
+ }
+
+ @Override
+ public Node getRootNode() throws RepositoryException {
+ return wrap(this.jcr.getRootNode());
+ }
+
+ public NodeIterator getNodes(String path, NodeIterator childs) throws RepositoryException {
+ if (isMountDirectParent(path)) {
+ List<Node> buffer = new ArrayList<>();
+ while (childs.hasNext()) {
+ Node child = childs.nextNode();
+ if (!isMount(child.getPath())) {
+ buffer.add(child);
+ }
+ }
+ for (String mountPoint : this.mountPoints) {
+ if (PathUtils.getParentPath(mountPoint).equals(path)) {
+ buffer.add(this.mount.getNode(mountPoint));
+ }
+ }
+ childs = new NodeIteratorAdapter(buffer);
+ }
+ return wrap(childs);
+ }
+
+ public boolean hasNodes(Node node) throws RepositoryException {
+ return isMountDirectParent(node.getPath()) || node.hasNodes();
+ }
+
+ @Override
+ public Session impersonate(Credentials credentials) throws LoginException, RepositoryException {
+ return this.repository.impersonate(credentials, this.jcr, this.mount);
+ }
+
+ @Override
+ public Node getNodeByUUID(String uuid) throws ItemNotFoundException, RepositoryException {
+ try {
+ return wrap(this.jcr.getNodeByUUID(uuid));
+ } catch (RepositoryException ex) {
+ try {
+ return wrap(this.mount.getNodeByUUID(uuid));
+ } catch (RepositoryException ignore) {
+ throw ex;
+ }
+ }
+ }
+
+ @Override
+ public Node getNodeByIdentifier(String id) throws ItemNotFoundException, RepositoryException {
+ try {
+ return wrap(this.jcr.getNodeByIdentifier(id));
+ } catch (RepositoryException ex) {
+ try {
+ return wrap(this.mount.getNodeByIdentifier(id));
+ } catch (RepositoryException ignore) {
+ throw ex;
+ }
+ }
+ }
+
+ @Override
+ public Item getItem(String absPath) throws PathNotFoundException, RepositoryException {
+ return wrap(isMount(absPath) ? this.mount.getItem(absPath) : this.jcr.getItem(absPath));
+ }
+
+ @Override
+ public Node getNode(String absPath) throws PathNotFoundException, RepositoryException {
+ return wrap(isMount(absPath) ? this.mount.getNode(absPath) : this.jcr.getNode(absPath));
+ }
+
+ @Override
+ public Property getProperty(String absPath) throws PathNotFoundException, RepositoryException {
+ return wrap(isMount(absPath) ? this.mount.getProperty(absPath) : this.jcr.getProperty(absPath));
+ }
+
+ @Override
+ public boolean itemExists(String absPath) throws RepositoryException {
+ return isMount(absPath) ? this.mount.itemExists(absPath) : this.jcr.itemExists(absPath);
+ }
+
+ @Override
+ public boolean nodeExists(String absPath) throws RepositoryException {
+ return isMount(absPath) ? this.mount.nodeExists(absPath) : this.jcr.nodeExists(absPath);
+ }
+
+ @Override
+ public boolean propertyExists(String absPath) throws RepositoryException {
+ return isMount(absPath) ? this.mount.propertyExists(absPath) : this.jcr.propertyExists(absPath);
+ }
+
+ @Override
+ public void removeItem(String absPath) throws VersionException, LockException, ConstraintViolationException, AccessDeniedException, RepositoryException {
+ if (sync != null) {
+ sync.remove(absPath);
+ }
+ if (isMount(absPath)) {
+ this.mount.removeItem(absPath);
+ } else {
+ this.jcr.removeItem(absPath);
+ if (isMountParent(absPath)) {
+ for (String mountPoint : this.mountPoints) {
+ if (mountPoint.startsWith((absPath + "/").replace("//", "/"))) {
+ for (NodeIterator iter = this.mount.getNode(mountPoint).getNodes(); iter.hasNext(); ) {
+ iter.nextNode().remove();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private volatile Set<String> sync;
+
+ private final static List<String> ignore = Arrays.asList("jcr:primaryType", "jcr:created", "jcr:createdBy");
+
+ @Override
+ public void save() throws AccessDeniedException, ItemExistsException, ReferentialIntegrityException, ConstraintViolationException, InvalidItemStateException, VersionException, LockException, NoSuchNodeTypeException, RepositoryException {
+ if (sync != null) {
+ for (String path : sync) {
+ if (this.jcr.nodeExists(path)) {
+ Node jcrNode = jcr.getNode(path);
+ Node mountNode = mount.nodeExists(path) ?
+ mount.getNode(path) :
+ mount.getNode(PathUtils.getParentPath(path)).addNode(PathUtils.getName(path), jcrNode.getPrimaryNodeType().getName());
+ for (PropertyIterator iter = jcrNode.getProperties(); iter.hasNext(); ) {
+ Property property = iter.nextProperty();
+ try {
+ if (property.isMultiple()) {
+ mountNode.setProperty(property.getName(), property.getValues());
+ } else {
+ mountNode.setProperty(property.getName(), property.getValue());
+ }
+ } catch (ConstraintViolationException ex) {
+ }
+ }
+ }
+ }
+ sync = null;
+ }
+
+ this.jcr.save();
+
+ this.mount.save();
+ }
+
+ @Override
+ public void refresh(boolean keepChanges) throws RepositoryException {
+ sync = null;
+ this.jcr.refresh(keepChanges);
+ this.mount.refresh(keepChanges);
+ }
+
+ public void refresh(String path, Item item, boolean keepChanges) throws RepositoryException {
+ sync = null;
+ item.refresh(keepChanges);
+ if (!isMount(path) && isMountParent(path)) {
+ this.mount.getRootNode().refresh(keepChanges);
+ }
+ }
+
+ @Override
+ public boolean hasPendingChanges() throws RepositoryException {
+ return this.jcr.hasPendingChanges() || this.mount.hasPendingChanges();
+ }
+
+ @Override
+ public ValueFactory getValueFactory() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return this.jcr.getValueFactory();
+ }
+
+ @Override
+ public boolean hasPermission(String absPath, String actions) throws RepositoryException {
+ return isMount(absPath) ? true : this.jcr.hasPermission(absPath, actions);
+ }
+
+ @Override
+ public void checkPermission(String absPath, String actions) throws AccessControlException, RepositoryException {
+ if (!isMount(absPath)) {
+ this.jcr.checkPermission(absPath, actions);
+ }
+ }
+
+ @Override
+ public boolean hasCapability(String methodName, Object target, Object[] arguments) throws RepositoryException {
+ return this.jcr.hasCapability(methodName, target, arguments);
+ }
+
+ @Override
+ public ContentHandler getImportContentHandler(String parentAbsPath, int uuidBehavior) throws PathNotFoundException, ConstraintViolationException, VersionException, LockException, RepositoryException {
+ if (isMount(parentAbsPath)) {
+ return this.mount.getImportContentHandler(parentAbsPath, uuidBehavior);
+ } else {
+ return this.jcr.getImportContentHandler(parentAbsPath, uuidBehavior);
+ }
+ }
+
+ @Override
+ public void importXML(String parentAbsPath, InputStream in, int uuidBehavior) throws IOException, PathNotFoundException, ItemExistsException, ConstraintViolationException, VersionException, InvalidSerializedDataException, LockException, RepositoryException {
+ if (isMount(parentAbsPath)) {
+ this.mount.importXML(parentAbsPath, in, uuidBehavior);
+ } else {
+ this.jcr.importXML(parentAbsPath, in, uuidBehavior);
+ }
+ }
+
+ @Override
+ public void exportSystemView(String absPath, ContentHandler contentHandler, boolean skipBinary, boolean noRecurse) throws PathNotFoundException, SAXException, RepositoryException {
+ if (isMount(absPath)) {
+ this.mount.exportSystemView(absPath, contentHandler, skipBinary, noRecurse);
+ } else {
+ this.jcr.exportSystemView(absPath, contentHandler, skipBinary, noRecurse);
+ }
+ }
+
+ @Override
+ public void exportSystemView(String absPath, OutputStream out, boolean skipBinary, boolean noRecurse) throws IOException, PathNotFoundException, RepositoryException {
+ if (isMount(absPath)) {
+ this.mount.exportSystemView(absPath, out, skipBinary, noRecurse);
+ } else {
+ this.jcr.exportSystemView(absPath, out, skipBinary, noRecurse);
+ }
+ }
+
+ @Override
+ public void exportDocumentView(String absPath, ContentHandler contentHandler, boolean skipBinary, boolean noRecurse) throws PathNotFoundException, SAXException, RepositoryException {
+ if (isMount(absPath)) {
+ this.mount.exportDocumentView(absPath, contentHandler, skipBinary, noRecurse);
+ } else {
+ this.jcr.exportDocumentView(absPath, contentHandler, skipBinary, noRecurse);
+ }
+ }
+
+ @Override
+ public void exportDocumentView(String absPath, OutputStream out, boolean skipBinary, boolean noRecurse) throws IOException, PathNotFoundException, RepositoryException {
+ if (isMount(absPath)) {
+ this.mount.exportDocumentView(absPath, out, skipBinary, noRecurse);
+ } else {
+ this.jcr.exportDocumentView(absPath, out, skipBinary, noRecurse);
+ }
+ }
+
+ @Override
+ public void setNamespacePrefix(String prefix, String uri) throws NamespaceException, RepositoryException {
+ this.jcr.setNamespacePrefix(prefix, uri);
+ this.mount.setNamespacePrefix(prefix, uri);
+ }
+
+ @Override
+ public String[] getNamespacePrefixes() throws RepositoryException {
+ return this.jcr.getNamespacePrefixes();
+ }
+
+ @Override
+ public String getNamespaceURI(String prefix) throws NamespaceException, RepositoryException {
+ return this.jcr.getNamespaceURI(prefix);
+ }
+
+ @Override
+ public String getNamespacePrefix(String uri) throws NamespaceException, RepositoryException {
+ return this.jcr.getNamespacePrefix(uri);
+ }
+
+ @Override
+ public void logout() {
+ this.jcr.logout();
+ this.mount.logout();
+ }
+
+ @Override
+ public boolean isLive() {
+ return this.jcr.isLive();
+ }
+
+ @Override
+ public void addLockToken(String lt) {
+ this.jcr.addLockToken(lt);
+ }
+
+ @Override
+ public String[] getLockTokens() {
+ return this.jcr.getLockTokens();
+ }
+
+ @Override
+ public void removeLockToken(String lt) {
+ this.jcr.removeLockToken(lt);
+ }
+
+ @Override
+ public AccessControlManager getAccessControlManager() throws UnsupportedRepositoryOperationException, RepositoryException {
+ AccessControlManager manager = this.jcr.getAccessControlManager();
+ return manager instanceof JackrabbitAccessControlManager ?
+ new ProxyJackrabbitAccessControlManager(this, (JackrabbitAccessControlManager) manager, (JackrabbitAccessControlManager) this.mount.getAccessControlManager()) :
+ new ProxyAccessControlManager<>(this, manager, this.mount.getAccessControlManager());
+ }
+
+ @Override
+ public RetentionManager getRetentionManager() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return this.jcr.getRetentionManager();
+ }
+
+ @Override
+ public void move(String srcAbsPath, String destAbsPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, RepositoryException {
+ if (isMount(srcAbsPath) && isMount(destAbsPath)) {
+ this.mount.move(srcAbsPath, destAbsPath);
+ } else if (!isMount(srcAbsPath) && !isMount(destAbsPath)) {
+ this.jcr.move(srcAbsPath, destAbsPath);
+ } else {
+ throw new IllegalStateException("Move between jcr and mount not supported");
+ }
+ }
+
+ @Override
+ public Workspace getWorkspace() {
+ return new ProxyWorkspace(this, this.jcr.getWorkspace(), this.mount.getWorkspace());
+ }
+
+ public Node addNode(String parent, String path, String name) throws RepositoryException {
+ if (isMount(path)) {
+ return wrap(this.mount.getNode(parent).addNode(name));
+ }
+ if (isMountParent(path)) {
+ this.mount.getNode(parent).addNode(name);
+ if (sync == null) {
+ sync = new HashSet<>();
+ }
+ sync.add(path);
+ }
+ return wrap(this.jcr.getNode(parent).addNode(name));
+ }
+
+ public Node addNode(String parent, String path, String name, String type) throws RepositoryException {
+ if (isMount(path)) {
+ return wrap(this.mount.getNode(parent).addNode(name, type));
+ }
+ if (isMountParent(path)) {
+ this.mount.getNode(parent).addNode(name, type);
+ if (sync == null) {
+ sync = new HashSet<>();
+ }
+ sync.add(path);
+ }
+ return wrap(this.jcr.getNode(parent).addNode(name, type));
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyUserManager.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyUserManager.java
new file mode 100644
index 0000000..7755789
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyUserManager.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import java.security.Principal;
+import java.util.Iterator;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.UnsupportedRepositoryOperationException;
+
+import org.apache.jackrabbit.api.JackrabbitSession;
+import org.apache.jackrabbit.api.security.user.Authorizable;
+import org.apache.jackrabbit.api.security.user.AuthorizableExistsException;
+import org.apache.jackrabbit.api.security.user.AuthorizableTypeException;
+import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.api.security.user.Query;
+import org.apache.jackrabbit.api.security.user.User;
+import org.apache.jackrabbit.api.security.user.UserManager;
+
+public class ProxyUserManager extends ProxyWrapper<UserManager> implements UserManager {
+ private final UserManager mount;
+
+ public ProxyUserManager(ProxySession<JackrabbitSession> mountSession, UserManager delegate, UserManager mount) {
+ super(mountSession, delegate);
+ this.mount = mount;
+ }
+
+
+ public Authorizable getAuthorizable(String id) throws RepositoryException {
+ return delegate.getAuthorizable(id);
+ }
+
+ public <T extends Authorizable> T getAuthorizable(String id, Class<T> authorizableClass) throws AuthorizableTypeException, RepositoryException {
+ return delegate.getAuthorizable(id, authorizableClass);
+ }
+
+ public Authorizable getAuthorizable(Principal principal) throws RepositoryException {
+ return delegate.getAuthorizable(principal);
+ }
+
+ public Authorizable getAuthorizableByPath(String path) throws UnsupportedRepositoryOperationException, RepositoryException {
+ return delegate.getAuthorizableByPath(path);
+ }
+
+ public Iterator<Authorizable> findAuthorizables(String relPath, String value) throws RepositoryException {
+ return delegate.findAuthorizables(relPath, value);
+ }
+
+ public Iterator<Authorizable> findAuthorizables(String relPath, String value, int searchType) throws RepositoryException {
+ return delegate.findAuthorizables(relPath, value, searchType);
+ }
+
+ public Iterator<Authorizable> findAuthorizables(Query query) throws RepositoryException {
+ return delegate.findAuthorizables(query);
+ }
+
+ public User createUser(String userID, String password) throws AuthorizableExistsException, RepositoryException {
+ User user = delegate.createUser(userID, password);
+ mount.createUser(userID, password, user.getPrincipal(), user.getPath());
+ return user;
+ }
+
+ public User createUser(String userID, String password, Principal principal, String intermediatePath) throws AuthorizableExistsException, RepositoryException {
+ User user = delegate.createUser(userID, password, principal, intermediatePath);
+ mount.createUser(userID, password, principal, user.getPath());
+ return user;
+ }
+
+ public User createSystemUser(String userID, String intermediatePath) throws AuthorizableExistsException, RepositoryException {
+ User user = delegate.createSystemUser(userID, intermediatePath);
+ mount.createSystemUser(userID, user.getPath());
+ return user;
+ }
+
+ public Group createGroup(String groupID) throws AuthorizableExistsException, RepositoryException {
+ Group group = delegate.createGroup(groupID);
+ mount.createGroup(groupID, group.getPrincipal(), group.getPath());
+ return group;
+ }
+
+ public Group createGroup(Principal principal) throws AuthorizableExistsException, RepositoryException {
+ Group group = delegate.createGroup(principal);
+ mount.createGroup(group.getID(), principal, group.getPath());
+ return group;
+ }
+
+ public Group createGroup(Principal principal, String intermediatePath) throws AuthorizableExistsException, RepositoryException {
+ Group group = delegate.createGroup(principal, intermediatePath);
+ mount.createGroup(principal, group.getPath());
+ return group;
+ }
+
+ public Group createGroup(String groupID, Principal principal, String intermediatePath) throws AuthorizableExistsException, RepositoryException {
+ Group group = delegate.createGroup(groupID, principal, intermediatePath);
+ mount.createGroup(groupID, principal, group.getPath());
+ return group;
+ }
+
+ public boolean isAutoSave() {
+ return delegate.isAutoSave();
+ }
+
+ public void autoSave(boolean enable) throws UnsupportedRepositoryOperationException, RepositoryException {
+ delegate.autoSave(enable);
+ mount.autoSave(enable);
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyWorkspace.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyWorkspace.java
new file mode 100644
index 0000000..75c1fb4
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyWorkspace.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.jcr.AccessDeniedException;
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.InvalidSerializedDataException;
+import javax.jcr.ItemExistsException;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.Workspace;
+import javax.jcr.lock.LockException;
+import javax.jcr.lock.LockManager;
+import javax.jcr.nodetype.ConstraintViolationException;
+import javax.jcr.nodetype.NodeTypeManager;
+import javax.jcr.observation.ObservationManager;
+import javax.jcr.query.QueryManager;
+import javax.jcr.version.Version;
+import javax.jcr.version.VersionException;
+import javax.jcr.version.VersionManager;
+
+import org.xml.sax.ContentHandler;
+
+public class ProxyWorkspace<T extends Workspace> extends ProxyWrapper<T> implements Workspace {
+ final T delegate2;
+
+ public ProxyWorkspace(ProxySession mountSession, T delegate, T delegate2) {
+ super(mountSession, delegate);
+ this.delegate2 = delegate2;
+ }
+
+ @Override
+ public Session getSession() {
+ return this.mountSession;
+ }
+
+ @Override
+ public String getName() {
+ return delegate.getName();
+ }
+
+ @Override
+ public QueryManager getQueryManager() throws RepositoryException {
+ return new ProxyQueryManager(this.mountSession, delegate.getQueryManager(), this.delegate2.getQueryManager());
+ }
+
+ // TODO: revisit the below
+
+ @Override
+ public ContentHandler getImportContentHandler(String parentAbsPath, int uuidBehavior) throws PathNotFoundException, ConstraintViolationException, VersionException, LockException, AccessDeniedException, RepositoryException {
+ return this.mountSession.getImportContentHandler(parentAbsPath, uuidBehavior);
+ }
+
+ @Override
+ public void importXML(String parentAbsPath, InputStream in, int uuidBehavior) throws IOException, VersionException, PathNotFoundException, ItemExistsException, ConstraintViolationException, InvalidSerializedDataException, LockException, AccessDeniedException, RepositoryException {
+ this.mountSession.importXML(parentAbsPath, in, uuidBehavior);
+ }
+
+ @Override
+ public void copy(String srcAbsPath, String destAbsPath) throws ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException {
+ if (mountSession.isMount(srcAbsPath) && mountSession.isMount(destAbsPath)) {
+ delegate2.copy(srcAbsPath, destAbsPath);
+ } else {
+ delegate.copy(srcAbsPath, destAbsPath);
+ }
+ }
+
+ @Override
+ public void copy(String srcWorkspace, String srcAbsPath, String destAbsPath) throws NoSuchWorkspaceException, ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException {
+ if (mountSession.isMount(srcAbsPath) && mountSession.isMount(destAbsPath)) {
+ delegate2.copy(srcWorkspace, srcAbsPath, destAbsPath);
+ } else {
+ delegate.copy(srcWorkspace, srcAbsPath, destAbsPath);
+ }
+ }
+
+ @Override
+ public void clone(String srcWorkspace, String srcAbsPath, String destAbsPath, boolean removeExisting) throws NoSuchWorkspaceException, ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException {
+ if (mountSession.isMount(srcAbsPath) && mountSession.isMount(destAbsPath)) {
+ delegate2.clone(srcWorkspace, srcAbsPath, destAbsPath, removeExisting);
+ } else {
+ delegate.clone(srcWorkspace, srcAbsPath, destAbsPath, removeExisting);
+ }
+ }
+
+ @Override
+ public void move(String srcAbsPath, String destAbsPath) throws ConstraintViolationException, VersionException, AccessDeniedException, PathNotFoundException, ItemExistsException, LockException, RepositoryException {
+ if (mountSession.isMount(srcAbsPath) && mountSession.isMount(destAbsPath)) {
+ delegate2.move(srcAbsPath, destAbsPath);
+ } else {
+ delegate.move(srcAbsPath, destAbsPath);
+ }
+ }
+
+ @Override
+ public void restore(Version[] versions, boolean removeExisting) throws ItemExistsException, UnsupportedRepositoryOperationException, VersionException, LockException, InvalidItemStateException, RepositoryException {
+ delegate.restore(versions, removeExisting);
+ }
+
+ @Override
+ public void createWorkspace(String name) throws AccessDeniedException, UnsupportedRepositoryOperationException, RepositoryException {
+ delegate.createWorkspace(name);
+ }
+
+ @Override
+ public void createWorkspace(String name, String srcWorkspace) throws AccessDeniedException, UnsupportedRepositoryOperationException, NoSuchWorkspaceException, RepositoryException {
+ delegate.createWorkspace(name, srcWorkspace);
+ }
+
+ @Override
+ public void deleteWorkspace(String name) throws AccessDeniedException, UnsupportedRepositoryOperationException, NoSuchWorkspaceException, RepositoryException {
+ delegate.deleteWorkspace(name);
+ }
+
+
+ @Override
+ public LockManager getLockManager() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return delegate.getLockManager();
+ }
+
+ @Override
+ public NamespaceRegistry getNamespaceRegistry() throws RepositoryException {
+ return new ProxyNamespaceRegistry(delegate.getNamespaceRegistry(), this.delegate2.getNamespaceRegistry());
+ }
+
+ @Override
+ public NodeTypeManager getNodeTypeManager() throws RepositoryException {
+ return new ProxyNodeTypeManager(delegate.getNodeTypeManager(), this.delegate2.getNodeTypeManager());
+ }
+
+ @Override
+ public ObservationManager getObservationManager() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return delegate.getObservationManager();
+ }
+
+ @Override
+ public VersionManager getVersionManager() throws UnsupportedRepositoryOperationException, RepositoryException {
+ return delegate.getVersionManager();
+ }
+
+ @Override
+ public String[] getAccessibleWorkspaceNames() throws RepositoryException {
+ return delegate.getAccessibleWorkspaceNames();
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyWrapper.java b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyWrapper.java
new file mode 100644
index 0000000..2de05ee
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/internal/mount/ProxyWrapper.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.internal.mount;
+
+import org.apache.jackrabbit.oak.commons.PathUtils;
+
+public class ProxyWrapper<T> {
+ final ProxySession<?> mountSession;
+ final T delegate;
+
+ public ProxyWrapper(ProxySession<?> mountSession, T delegate) {
+ this.mountSession = mountSession;
+ this.delegate = delegate;
+ }
+
+ String concat(String parent, String relPath) {
+ if (relPath != null) {
+ while (relPath.startsWith("/")) {
+ relPath = relPath.substring(1);
+ }
+ }
+ return PathUtils.concat(parent, relPath);
+ }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/spi/RepositoryMount.java b/src/main/java/org/apache/sling/jcr/base/spi/RepositoryMount.java
new file mode 100644
index 0000000..f009877
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/spi/RepositoryMount.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base.spi;
+
+import org.apache.jackrabbit.api.JackrabbitRepository;
+
+public interface RepositoryMount extends JackrabbitRepository
+{
+ String PARENT_SESSION_KEY = "org.apache.sling.jcr.base.RepositoryMount.PARENT_SESSION";
+ String MOUNT_POINTS_KEY = "org.apache.sling.jcr.base.RepositoryMount.MOUNT_POINTS";
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/spi/package-info.java b/src/main/java/org/apache/sling/jcr/base/spi/package-info.java
new file mode 100644
index 0000000..a34c77c
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/spi/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/**
+ * The {@code org.apache.sling.jcr.base.spi} package provides a
+ * way to bifurcate requests to subpaths to a mount provider.
+ */
+@org.osgi.annotation.versioning.Version("0.1.0")
+package org.apache.sling.jcr.base.spi;
+
+