| /* |
| * 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.api.resource; |
| |
| import java.text.MessageFormat; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.NoSuchElementException; |
| |
| import org.apache.sling.api.wrappers.ValueMapDecorator; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| |
| /** |
| * The <code>ResourceUtil</code> class provides helper methods dealing with |
| * resources. |
| * <p> |
| * This class is not intended to be extended or instantiated because it just |
| * provides static utility methods not intended to be overwritten. |
| */ |
| public class ResourceUtil { |
| |
| /** |
| * Resolves relative path segments '.' and '..' in the absolute path. |
| * Returns {@code null} if not possible (.. points above root) or if path is not |
| * absolute. |
| * |
| * @param path The path to normalize |
| * @return The normalized path or {@code null}. |
| */ |
| public static @Nullable String normalize(@NotNull String path) { |
| |
| // don't care for empty paths |
| if (path.length() == 0) { |
| return path; |
| } |
| |
| // prepare the path buffer with trailing slash (simplifies impl) |
| int absOffset = (path.charAt(0) == '/') ? 0 : 1; |
| char[] buf = new char[path.length() + 1 + absOffset]; |
| if (absOffset == 1) { |
| buf[0] = '/'; |
| } |
| path.getChars(0, path.length(), buf, absOffset); |
| buf[buf.length - 1] = '/'; |
| |
| int lastSlash = 0; // last slash in path |
| int numDots = 0; // number of consecutive dots after last slash |
| |
| int bufPos = 0; |
| for (int bufIdx = lastSlash; bufIdx < buf.length; bufIdx++) { |
| char c = buf[bufIdx]; |
| if (c == '/') { |
| if (numDots == 2) { |
| if (bufPos == 0) { |
| return null; |
| } |
| |
| do { |
| bufPos--; |
| } while (bufPos > 0 && buf[bufPos] != '/'); |
| } |
| |
| lastSlash = bufIdx; |
| numDots = 0; |
| } else if (c == '.' && useDot(buf, bufIdx) && numDots < 2) { |
| numDots++; |
| } else { |
| // find the next slash |
| int nextSlash = bufIdx + 1; |
| while (nextSlash < buf.length && buf[nextSlash] != '/') { |
| nextSlash++; |
| } |
| |
| // append up to the next slash (or end of path) |
| if (bufPos < lastSlash) { |
| int segLen = nextSlash - bufIdx + 1; |
| System.arraycopy(buf, lastSlash, buf, bufPos, segLen); |
| bufPos += segLen; |
| } else { |
| bufPos = nextSlash; |
| } |
| |
| numDots = 0; |
| lastSlash = nextSlash; |
| bufIdx = nextSlash; |
| } |
| } |
| |
| String resolved; |
| if (bufPos == 0 && numDots == 0) { |
| resolved = (absOffset == 0) ? "/" : ""; |
| } else if ((bufPos - absOffset) == path.length()) { |
| resolved = path; |
| } else { |
| resolved = new String(buf, absOffset, bufPos - absOffset); |
| } |
| |
| return resolved; |
| } |
| |
| // use this dot only if followed by / |
| // don't use if followed by neither . nor / |
| // keep checking till a non-dot is found |
| private static boolean useDot(char[] buf, int bufIdx) { |
| while(bufIdx < buf.length -1) { |
| if(buf[bufIdx] == '/') { |
| return true; |
| } |
| else if(buf[bufIdx] != '.') { |
| return false; |
| } |
| bufIdx++; |
| } |
| return true; |
| } |
| /** |
| * Utility method returns the parent path of the given <code>path</code>, |
| * which is normalized by {@link #normalize(String)} before resolving the |
| * parent. |
| * |
| * @param path The path whose parent is to be returned. |
| * @return <code>null</code> if <code>path</code> is the root path ( |
| * <code>/</code>) or if <code>path</code> is a single name |
| * containing no slash (<code>/</code>) characters. |
| * @throws IllegalArgumentException If the path cannot be normalized by the |
| * {@link #normalize(String)} method. |
| * @throws NullPointerException If <code>path</code> is <code>null</code>. |
| */ |
| public static @Nullable String getParent(@NotNull String path) { |
| if ("/".equals(path)) { |
| return null; |
| } |
| |
| // normalize path (remove . and ..) |
| path = normalize(path); |
| |
| // if normalized to root, there is no parent |
| if (path == null || "/".equals(path)) { |
| return null; |
| } |
| |
| String workspaceName = null; |
| |
| final int wsSepPos = path.indexOf(":/"); |
| if (wsSepPos != -1) { |
| workspaceName = path.substring(0, wsSepPos); |
| path = path.substring(wsSepPos + 1); |
| } |
| |
| // find the last slash, after which to cut off |
| int lastSlash = path.lastIndexOf('/'); |
| if (lastSlash < 0) { |
| // no slash in the path |
| return null; |
| } else if (lastSlash == 0) { |
| // parent is root |
| if (workspaceName != null) { |
| return workspaceName + ":/"; |
| } |
| return "/"; |
| } |
| |
| String parentPath = path.substring(0, lastSlash); |
| if (workspaceName != null) { |
| return workspaceName + ":" + parentPath; |
| } |
| return parentPath; |
| } |
| |
| /** |
| * Utility method returns the ancestor's path at the given <code>level</code> |
| * relative to <code>path</code>, which is normalized by {@link #normalize(String)} |
| * before resolving the ancestor. |
| * |
| * <ul> |
| * <li><code>level</code> = 0 returns the <code>path</code>.</li> |
| * <li><code>level</code> = 1 returns the parent of <code>path</code>, if it exists, <code>null</code> otherwise.</li> |
| * <li><code>level</code> = 2 returns the grandparent of <code>path</code>, if it exists, <code>null</code> otherwise.</li> |
| * </ul> |
| * |
| * @param path The path whose ancestor is to be returned. |
| * @param level The relative level of the ancestor, relative to <code>path</code>. |
| * @return <code>null</code> if <code>path</code> doesn't have an ancestor at the |
| * specified <code>level</code>. |
| * @throws IllegalArgumentException If the path cannot be normalized by the |
| * {@link #normalize(String)} method or if <code>level</code> < 0. |
| * @throws NullPointerException If <code>path</code> is <code>null</code>. |
| * @since 2.2 (Sling API Bundle 2.2.0) |
| */ |
| public static String getParent(final String path, final int level) { |
| if ( level < 0 ) { |
| throw new IllegalArgumentException("level must be non-negative"); |
| } |
| String result = path; |
| for(int i=0; i<level; i++) { |
| result = getParent(result); |
| if ( result == null ) { |
| break; |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Utility method returns the parent resource of the resource. |
| * |
| * @param rsrc The resource to get the parent of. |
| * @return The parent resource or null if the rsrc is the root. |
| * @throws NullPointerException If <code>rsrc</code> is <code>null</code>. |
| * @throws org.apache.sling.api.SlingException If an error occurs trying to |
| * get the resource object from the path. |
| * @throws IllegalStateException if the resource resolver has already been |
| * closed}. |
| * @deprecated since 2.1.0, use {@link Resource#getParent()} instead |
| */ |
| @Deprecated |
| public static @Nullable Resource getParent(@NotNull Resource rsrc) { |
| return rsrc.getParent(); |
| } |
| |
| /** |
| * Utility method returns the name of the resource. |
| * |
| * @param rsrc The resource to get the name from. |
| * @return The name of the resource |
| * @throws NullPointerException If <code>rsrc</code> is <code>null</code>. |
| * @deprecated since 2.1.0, use {@link Resource#getName()} instead |
| */ |
| @Deprecated |
| public static @NotNull String getName(@NotNull Resource rsrc) { |
| /* |
| * Same as AbstractResource.getName() implementation to prevent problems |
| * if there are implementations of the pre-2.1.0 Resource interface in |
| * the framework. |
| */ |
| return getName(rsrc.getPath()); |
| } |
| |
| /** |
| * Utility method returns the name of the given <code>path</code>, which is |
| * normalized by {@link #normalize(String)} before resolving the name. |
| * |
| * @param path The path whose name (the last path element) is to be |
| * returned. |
| * @return The empty string if <code>path</code> is the root path ( |
| * <code>/</code>) or if <code>path</code> is a single name |
| * containing no slash (<code>/</code>) characters. |
| * @throws IllegalArgumentException If the path cannot be normalized by the |
| * {@link #normalize(String)} method. |
| * @throws NullPointerException If <code>path</code> is <code>null</code>. |
| */ |
| public static @NotNull String getName(@NotNull String path) { |
| if ("/".equals(path)) { |
| return ""; |
| } |
| |
| // normalize path (remove . and ..) |
| path = normalize(path); |
| if ("/".equals(path)) { |
| return ""; |
| } |
| |
| // find the last slash |
| return path.substring(path.lastIndexOf('/') + 1); |
| } |
| |
| /** |
| * Returns <code>true</code> if the resource <code>res</code> is a synthetic |
| * resource. |
| * <p> |
| * This method checks whether the resource is an instance of the |
| * <code>org.apache.sling.resource.SyntheticResource</code> class. |
| * |
| * @param res The <code>Resource</code> to check whether it is a synthetic |
| * resource. |
| * @return <code>true</code> if <code>res</code> is a synthetic resource. |
| * <code>false</code> is returned if <code>res</code> is |
| * <code>null</code> or not an instance of the |
| * <code>org.apache.sling.resource.SyntheticResource</code> class. |
| */ |
| public static boolean isSyntheticResource(@NotNull Resource res) { |
| if (res instanceof SyntheticResource) { |
| return true; |
| } |
| |
| if (!(res instanceof ResourceWrapper)) { |
| return false; |
| } |
| |
| do { |
| res = ((ResourceWrapper) res).getResource(); |
| } while (res instanceof ResourceWrapper); |
| |
| return res instanceof SyntheticResource; |
| } |
| |
| /** |
| * Returns <code>true</code> if the resource <code>res</code> is a "star |
| * resource". A <i>star resource</i> is a resource returned from the |
| * <code>ResourceResolver.resolve(HttpServletRequest)</code> whose path |
| * terminates in a <code>/*</code>. Generally such resource result from |
| * requests to something like <code>/some/path/*</code> or |
| * <code>/some/path/*.html</code> which may be used web applications to |
| * uniformly handle resources to be created. |
| * <p> |
| * This method checks whether the resource path ends with a <code>/*</code> |
| * indicating such a star resource. |
| * |
| * @param res The <code>Resource</code> to check whether it is a star |
| * resource. |
| * @return <code>true</code> if <code>res</code> is to be considered a star |
| * resource. |
| * @throws NullPointerException if <code>res</code> is <code>null</code>. |
| */ |
| public static boolean isStarResource(@NotNull Resource res) { |
| return res.getPath().endsWith("/*"); |
| } |
| |
| /** |
| * Returns <code>true</code> if the resource <code>res</code> is a |
| * non-existing resource. |
| * <p> |
| * This method checks the resource type of the resource to match the |
| * well-known resource type <code>sling:nonexisting</code> of the |
| * <code>NonExistingResource</code> class defined in the Sling API. |
| * |
| * @param res The <code>Resource</code> to check whether it is a |
| * non-existing resource. |
| * @return <code>true</code> if <code>res</code> is to be considered a |
| * non-existing resource. |
| * @throws NullPointerException if <code>res</code> is <code>null</code>. |
| */ |
| public static boolean isNonExistingResource(@NotNull Resource res) { |
| return Resource.RESOURCE_TYPE_NON_EXISTING.equals(res.getResourceType()); |
| } |
| |
| /** |
| * Returns an <code>Iterator</code> of {@link Resource} objects loaded from |
| * the children of the given <code>Resource</code>. |
| * <p> |
| * This is a convenience method for |
| * {@link ResourceResolver#listChildren(Resource)}. |
| * |
| * @param parent The {@link Resource Resource} whose children are requested. |
| * @return An <code>Iterator</code> of {@link Resource} objects. |
| * @throws NullPointerException If <code>parent</code> is <code>null</code>. |
| * @throws IllegalStateException if the resource resolver has already been |
| * closed}. |
| * @throws org.apache.sling.api.SlingException If any error occurs acquiring |
| * the child resource iterator. |
| * @see ResourceResolver#listChildren(Resource) |
| * @deprecated since 2.1.0, use {@link Resource#listChildren()} instead |
| */ |
| @Deprecated |
| public static @NotNull Iterator<Resource> listChildren(@NotNull Resource parent) { |
| // directly call the resource resolver to ensure the correct result |
| return parent.getResourceResolver().listChildren(parent); |
| } |
| |
| /** |
| * Returns an <code>ValueMap</code> object for the given |
| * <code>Resource</code>. This method calls {@link Resource#getValueMap()}. |
| * If <code>null</code> is provided as the resource an empty map is returned as |
| * well. |
| * <p>For backward compatibility reasons the map returned is not immutable, |
| * but it is not recommend to rely on this behavior.</p> |
| * |
| * @param res The <code>Resource</code> to adapt to the value map. |
| * @return A value map. |
| */ |
| public static @NotNull ValueMap getValueMap(final Resource res) { |
| if ( res == null ) { |
| // use empty map |
| return new ValueMapDecorator(new HashMap<String, Object>()); |
| } |
| return res.getValueMap(); |
| } |
| |
| /** |
| * Helper method, which returns the given resource type as returned from the |
| * {@link org.apache.sling.api.resource.Resource#getResourceType()} as a |
| * relative path. |
| * |
| * @param type The resource type to be converted into a path |
| * @return The resource type as a path. |
| * @since 2.0.6 (Sling API Bundle 2.0.6) |
| */ |
| public static @NotNull String resourceTypeToPath(@NotNull final String type) { |
| return type.replace(':', '/'); |
| } |
| |
| /** |
| * Returns the super type of the given resource type. This method converts |
| * the resource type to a resource path by calling |
| * {@link #resourceTypeToPath(String)} and uses the |
| * <code>resourceResolver</code> to get the corresponding resource. If the |
| * resource exists, the {@link Resource#getResourceSuperType()} method is |
| * called. |
| * |
| * @param resourceResolver The <code>ResourceResolver</code> used to access |
| * the resource whose path (relative or absolute) is given by the |
| * <code>resourceType</code> parameter. |
| * @param resourceType The resource type whose super type is to be returned. |
| * This type is turned into a path by calling the |
| * {@link #resourceTypeToPath(String)} method before trying to |
| * get the resource through the <code>resourceResolver</code>. |
| * @return the super type of the <code>resourceType</code> or |
| * <code>null</code> if the resource type does not exists or returns |
| * <code>null</code> for its super type. |
| * @throws IllegalStateException if the resource resolver has already been |
| * closed}. |
| * @since 2.0.6 (Sling API Bundle 2.0.6) |
| * @deprecated Use {@link ResourceResolver#getParentResourceType(String)} |
| */ |
| @Deprecated |
| public static @Nullable String getResourceSuperType( |
| final @NotNull ResourceResolver resourceResolver, final String resourceType) { |
| return resourceResolver.getParentResourceType(resourceType); |
| } |
| |
| /** |
| * Returns the super type of the given resource. This method checks first if |
| * the resource itself knows its super type by calling |
| * {@link Resource#getResourceSuperType()}. If that returns |
| * <code>null</code> {@link #getResourceSuperType(ResourceResolver, String)} |
| * is invoked with the resource type of the resource. |
| * |
| * @param resource The resource to return the resource super type for. |
| * @return the super type of the <code>resource</code> or <code>null</code> |
| * if no super type could be computed. |
| * @throws IllegalStateException if the resource resolver has already been |
| * closed}. |
| * @since 2.0.6 (Sling API Bundle 2.0.6) |
| * @deprecated Use {@link ResourceResolver#getParentResourceType(Resource)} |
| */ |
| @Deprecated |
| public static @Nullable String findResourceSuperType(@NotNull final Resource resource) { |
| if ( resource == null ) { |
| return null; |
| } |
| return resource.getResourceResolver().getParentResourceType(resource); |
| } |
| |
| /** |
| * Check if the resource is of the given type. This method first checks the |
| * resource type of the resource, then its super resource type and continues |
| * to go up the resource super type hierarchy. |
| * |
| * In case the type of the given resource or the given resource type starts with one of the resource resolver's search paths |
| * it is converted to a relative resource type by stripping off the resource resolver's search path |
| * before doing the comparison. |
| * |
| * @param resource the resource to check |
| * @param resourceType the resource type to check the resource against |
| * @return <code>false</code> if <code>resource</code> is <code>null</code>. |
| * Otherwise returns the result of calling |
| * {@link Resource#isResourceType(String)} with the given |
| * <code>resourceType</code>. |
| * @throws IllegalStateException if the resource resolver has already been |
| * closed}. |
| * @since 2.0.6 (Sling API Bundle 2.0.6) |
| * @deprecated Use {@link ResourceResolver#isResourceType(Resource, String)} |
| */ |
| @Deprecated |
| public static boolean isA(@NotNull final Resource resource, final String resourceType) { |
| if ( resource == null ) { |
| return false; |
| } |
| return resource.getResourceResolver().isResourceType(resource, resourceType); |
| } |
| |
| /** |
| * Return an iterator for objects of the specified type. A new iterator is |
| * returned which tries to adapt the provided resources to the given type |
| * (using {@link Resource#adaptTo(Class)}. If a resource in the original |
| * iterator is not adaptable to the given class, this object is skipped. |
| * This implies that the number of objects returned by the new iterator |
| * might be less than the number of resource objects. |
| * |
| * @param iterator A resource iterator. |
| * @param <T> The adapted type |
| * @param type The adapted type |
| * @return An iterator of the adapted objects |
| * @since 2.0.6 (Sling API Bundle 2.0.6) |
| */ |
| public static @NotNull <T> Iterator<T> adaptTo(final @NotNull Iterator<Resource> iterator, |
| final Class<T> type) { |
| return new Iterator<T>() { |
| |
| private T nextObject = seek(); |
| |
| @Override |
| public boolean hasNext() { |
| return nextObject != null; |
| } |
| |
| @Override |
| public T next() { |
| if (!hasNext()) { |
| throw new NoSuchElementException(); |
| } |
| final T object = nextObject; |
| nextObject = seek(); |
| return object; |
| } |
| |
| @Override |
| public void remove() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| private T seek() { |
| T result = null; |
| while (result == null && iterator.hasNext()) { |
| final Resource r = iterator.next(); |
| result = r.adaptTo(type); |
| } |
| return result; |
| } |
| }; |
| } |
| |
| /** |
| * Creates or gets the resource at the given path. |
| * |
| * @param resolver The resource resolver to use for creation |
| * @param path The full path to be created |
| * @param resourceType The optional resource type of the final resource to create |
| * @param intermediateResourceType THe optional resource type of all intermediate resources |
| * @param autoCommit If set to true, a commit is performed after each resource creation. |
| * @return The resource for the path. |
| * @throws org.apache.sling.api.SlingException If an error occurs trying to |
| * get/create the resource object from the path. |
| * @throws IllegalStateException if the resource resolver has already been |
| * closed}. |
| * @throws PersistenceException If a persistence error occurs. |
| * @since 2.3.0 (Sling API Bundle 2.4.0) |
| */ |
| public static @NotNull Resource getOrCreateResource( |
| final @NotNull ResourceResolver resolver, |
| final @NotNull String path, |
| final String resourceType, |
| final String intermediateResourceType, |
| final boolean autoCommit) |
| throws PersistenceException { |
| final Map<String, Object> props; |
| if ( resourceType == null ) { |
| props = null; |
| } else { |
| props = Collections.singletonMap(ResourceResolver.PROPERTY_RESOURCE_TYPE, (Object)resourceType); |
| } |
| return getOrCreateResource(resolver, path, props, intermediateResourceType, autoCommit); |
| } |
| |
| /** |
| * Creates or gets the resource at the given path. |
| * If an exception occurs, it retries the operation up to five times if autoCommit is enabled. |
| * In this case, {@link ResourceResolver#revert()} is called on the resolver before the |
| * creation is retried. |
| * |
| * @param resolver The resource resolver to use for creation |
| * @param path The full path to be created |
| * @param resourceProperties The optional resource properties of the final resource to create |
| * @param intermediateResourceType THe optional resource type of all intermediate resources |
| * @param autoCommit If set to true, a commit is performed after each resource creation. |
| * @return The resource for the path. |
| * @throws org.apache.sling.api.SlingException If an error occurs trying to |
| * get/create the resource object from the path. |
| * @throws IllegalStateException if the resource resolver has already been |
| * closed}. |
| * @throws PersistenceException If a persistence error occurs. |
| * @since 2.3.0 (Sling API Bundle 2.4.0) |
| */ |
| public static @NotNull Resource getOrCreateResource( |
| final @NotNull ResourceResolver resolver, |
| final @NotNull String path, |
| final Map<String, Object> resourceProperties, |
| final String intermediateResourceType, |
| final boolean autoCommit) |
| throws PersistenceException { |
| PersistenceException mostRecentPE = null; |
| for(int i=0;i<5;i++) { |
| try { |
| return ResourceUtil.getOrCreateResourceInternal(resolver, |
| path, |
| resourceProperties, |
| intermediateResourceType, |
| autoCommit); |
| } catch ( final PersistenceException pe ) { |
| if ( autoCommit ) { |
| // in case of exception, revert to last clean state and retry |
| resolver.revert(); |
| resolver.refresh(); |
| mostRecentPE = pe; |
| } else { |
| throw pe; |
| } |
| } |
| } |
| throw mostRecentPE; |
| } |
| |
| /** |
| * Creates or gets the resource at the given path. |
| * |
| * @param resolver The resource resolver to use for creation |
| * @param path The full path to be created |
| * @param resourceProperties The optional resource properties of the final resource to create |
| * @param intermediateResourceType THe optional resource type of all intermediate resources |
| * @param autoCommit If set to true, a commit is performed after each resource creation. |
| * @return The resource for the path. |
| * @throws org.apache.sling.api.SlingException If an error occurs trying to |
| * get/create the resource object from the path. |
| * @throws IllegalStateException if the resource resolver has already been |
| * closed}. |
| * @throws PersistenceException If a persistence error occurs. |
| */ |
| private static Resource getOrCreateResourceInternal( |
| final ResourceResolver resolver, |
| final String path, |
| final Map<String, Object> resourceProperties, |
| final String intermediateResourceType, |
| final boolean autoCommit) |
| throws PersistenceException { |
| Resource rsrc = resolver.getResource(path); |
| if ( rsrc == null ) { |
| final int lastPos = path.lastIndexOf('/'); |
| final String name = path.substring(lastPos + 1); |
| |
| final Resource parentResource; |
| if ( lastPos == 0 ) { |
| parentResource = resolver.getResource("/"); |
| } else { |
| final String parentPath = path.substring(0, lastPos); |
| parentResource = getOrCreateResource(resolver, |
| parentPath, |
| intermediateResourceType, |
| intermediateResourceType, |
| autoCommit); |
| } |
| if ( autoCommit ) { |
| resolver.refresh(); |
| } |
| try { |
| int retry = 5; |
| while ( retry > 0 && rsrc == null ) { |
| rsrc = resolver.create(parentResource, name, resourceProperties); |
| // check for SNS |
| if ( !name.equals(rsrc.getName()) ) { |
| resolver.refresh(); |
| resolver.delete(rsrc); |
| rsrc = resolver.getResource(parentResource, name); |
| } |
| retry--; |
| } |
| if ( rsrc == null ) { |
| throw new PersistenceException("Unable to create resource."); |
| } |
| } catch ( final PersistenceException pe ) { |
| // this could be thrown because someone else tried to create this |
| // node concurrently |
| resolver.refresh(); |
| rsrc = resolver.getResource(parentResource, name); |
| if ( rsrc == null ) { |
| throw pe; |
| } |
| } |
| if ( autoCommit ) { |
| try { |
| resolver.commit(); |
| resolver.refresh(); |
| rsrc = resolver.getResource(parentResource, name); |
| } catch ( final PersistenceException pe ) { |
| // try again - maybe someone else did create the resource in the meantime |
| // or we ran into Jackrabbit's stale item exception in a clustered environment |
| resolver.revert(); |
| resolver.refresh(); |
| rsrc = resolver.getResource(parentResource, name); |
| if ( rsrc == null ) { |
| rsrc = resolver.create(parentResource, name, resourceProperties); |
| resolver.commit(); |
| } |
| } |
| } |
| } |
| return rsrc; |
| } |
| |
| /** |
| * Create a unique name for a child of the <code>parent</code>. |
| * Creates a unique name and test if child already exists. |
| * If child resource with the same name exists, iterate until a unique one is found. |
| * |
| * @param parent The parent resource |
| * @param name The name of the child resource |
| * @return a unique non-existing name for child resource for a given <code>parent</code> |
| * |
| * @throws PersistenceException if it can not find unique name for child resource. |
| * @throws NullPointerException if <code>parent</code> is null |
| * @throws IllegalStateException if the resource resolver has already been |
| * closed}. |
| * @since 2.5 (Sling API Bundle 2.7.0) |
| */ |
| public static String createUniqueChildName(final Resource parent, final String name) |
| throws PersistenceException { |
| if (parent.getChild(name) != null) { |
| // leaf node already exists, create new unique name |
| String childNodeName = null; |
| int i = 0; |
| do { |
| childNodeName = name + String.valueOf(i); |
| //just so that it does not run into an infinite loop |
| // this should not happen though :) |
| if (i == Integer.MAX_VALUE) { |
| String message = MessageFormat.format("can not find a unique name {0} for {1}", name, parent.getPath()); |
| throw new PersistenceException(message); |
| } |
| i++; |
| } while (parent.getChild(childNodeName) != null); |
| |
| return childNodeName; |
| } |
| return name; |
| } |
| |
| /** |
| * Unwrap the resource and return the wrapped implementation. |
| * @param rsrc The resource to unwrap |
| * @return The unwrapped resource |
| * @since 2.5 (Sling API Bundle 2.7.0) |
| */ |
| public static @NotNull Resource unwrap(final @NotNull Resource rsrc) { |
| Resource result = rsrc; |
| while ( result instanceof ResourceWrapper ) { |
| result = ((ResourceWrapper)result).getResource(); |
| } |
| return result; |
| } |
| |
| /** |
| * A batch resource remover deletes resources in batches. Once the batch |
| * size (threshold) is reached, an intermediate commit is performed. Resource |
| * trees are deleted resource by resource starting with the deepest children first. |
| * Once all resources have been passed to the batch resource remover, a final |
| * commit needs to be called on the resource resolver. |
| * @since 2.6 (Sling API Bundle 2.8.0) |
| */ |
| public static class BatchResourceRemover { |
| |
| private final int max; |
| |
| private int count; |
| |
| public BatchResourceRemover(final int batchSize) { |
| this.max = (batchSize < 1 ? 50 : batchSize); |
| } |
| |
| public void delete(@NotNull final Resource rsrc) |
| throws PersistenceException { |
| final ResourceResolver resolver = rsrc.getResourceResolver(); |
| for(final Resource child : rsrc.getChildren()) { |
| delete(child); |
| } |
| resolver.delete(rsrc); |
| count++; |
| if ( count >= max ) { |
| resolver.commit(); |
| count = 0; |
| } |
| } |
| } |
| |
| /** |
| * Create a batch resource remover. |
| * A batch resource remove can be used to delete resources in batches. |
| * Once the passed in threshold of deleted resources is reached, an intermediate |
| * commit is called on the resource resolver. In addition the batch remover |
| * deletes a resource recursively. |
| * Once all resources to delete are passed to the remover, a final commit needs |
| * to be call on the resource resolver. |
| * @param threshold The threshold for the intermediate saves. |
| * @return A new batch resource remover. |
| * Since 2.6 |
| */ |
| public static @NotNull BatchResourceRemover getBatchResourceRemover(final int threshold) { |
| return new BatchResourceRemover(threshold); |
| } |
| } |