You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2012/07/30 18:20:18 UTC
svn commit: r1367147 - in /sling/trunk:
bundles/api/src/main/java/org/apache/sling/api/resource/
bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/
bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/
bundles/jcr/re...
Author: cziegeler
Date: Mon Jul 30 16:20:17 2012
New Revision: 1367147
URL: http://svn.apache.org/viewvc?rev=1367147&view=rev
Log:
SLING-2530 : Implement CRUD based on resources
Added:
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java (with props)
Modified:
sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifiableValueMap.java
sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java
sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceProviderFactory.java
sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java
sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderFactory.java
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java
sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderList.java
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/RootResourceProviderEntry.java
sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderListTest.java
sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java
sling/trunk/launchpad/integration-tests/src/main/resources/integration-test/crud/crud-test.jsp
Modified: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifiableValueMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifiableValueMap.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifiableValueMap.java (original)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ModifiableValueMap.java Mon Jul 30 16:20:17 2012
@@ -21,42 +21,37 @@ package org.apache.sling.api.resource;
/**
* The <code>ModifiableValueMap</code> is an extension
* of the {@link ValueMap} which allows to modify and
- * persist properties. All changes to this map are transient
- * and only available through this map instance.
- *
- * Changes can be pushed into the transient persistence layer
- * with a call to {@link #update()}. This causes the changes
- * to be stored in the persistence layer for committing. Once
- * {@link ResourceResolver#commit()} is called, the
+ * persist properties. All changes to this map are
+ * stored in the transient layer of the resource resolver
+ * or more precisely in the transient layer of the
+ * resource provider managing this resource.
+ * <p>
+ * Once{@link ResourceResolver#commit()} is called, the
* changes are finally persisted.
- *
- * Note, that each time you call {@link Resource#adaptTo(Class)}
- * you get a new map instance which does not share modified
- * properties with other representations until {@link #update()}
- * is called. In general, to avoid confusion, it's better to use
- * one modifiable value map for a resource per resource resolver.
- *
+ * <p>
+ * The modifiable value map is only changeable through
+ * one of these methods
+ * <ul>
+ * <li>{@link #put(String, Object)}</li>
+ * <li>{@link #putAll(java.util.Map)}</li>
+ * <li>{@link #remove(Object)}</li>
+ * </ul>
+ * <p>
+ * The map is not modifiable through the collection provided
+ * by
+ * <ul>
+ * <li>{@link #entrySet()}</li>
+ * <li>{@link #keySet()}</li>
+ * <li>{@link #values()}</li>
+ * </ul>
+ * or one of these methods:
+ * <ul>
+ * <li>{@link #clear()}</li>
+ * </ul>
+ * <p>
* @since 2.2
*/
public interface ModifiableValueMap extends ValueMap {
- /**
- * Persists the changes in the transient persistence layer.
- * Once update is called this map has no temporary changes
- * any more.
- * A call to {@link ResourceResolver#commit()} is required
- * to permanently persist the changes.
- * @throws PersistenceException If the changes can't be persisted.
- */
- void update() throws PersistenceException;
-
- /**
- * Revert temporary changes.
- */
- void revert();
-
- /**
- * Are there any temporary changes?
- */
- boolean hasChanges();
+ // just a marker
}
Modified: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java (original)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java Mon Jul 30 16:20:17 2012
@@ -29,14 +29,19 @@ import java.util.Map;
* {@link ResourceProvider} interface and {@link ResourceProvider}s
* returned through a {@link ResourceProviderFactory}.
*
- * TODO - what should the resource provider do, if the language is not supported?
- * TODO - is returning null allowed?
- *
* @since 2.2.0
*/
public interface QueriableResourceProvider {
/**
+ * The name of the service registration property containing the supported
+ * languages of the resource provider (value is "provider.query.languages").
+ * If the resource provider is delivered by a {@link ResourceProviderFactory}
+ * this property should be declared on the factory.
+ */
+ String LANGUAGES = "provider.query.languages";
+
+ /**
* Searches for resources using the given query formulated in the given
* language.
* <p>
@@ -50,10 +55,11 @@ public interface QueriableResourceProvid
* @param query The query string to use to find the resources.
* @param language The language in which the query is formulated.
* @return An <code>Iterator</code> of {@link Resource} objects matching the
- * query.
+ * query. If no resources match, <code>null</code> might be
+ * returned instead of an empty iterator.
* @throws QuerySyntaxException If the query is not syntactically correct
- * according to the query language indicator of if the query
- * language is not supported.
+ * according to the query language indicator or if the query
+ * language is not supported as specified in {@link #LANGUAGES}.
* @throws org.apache.sling.api.SlingException If an error occurrs querying
* for the resources.
* @throws IllegalStateException if this resource provider has already been
@@ -78,10 +84,11 @@ public interface QueriableResourceProvid
* @param query The query string to use to find the resources.
* @param language The language in which the query is formulated.
* @return An <code>Iterator</code> of <code>Map</code> instances providing
- * access to the query result.
+ * access to the query result. If no resources match, <code>null</code>
+ * might be returned instead of an empty iterator.
* @throws QuerySyntaxException If the query is not syntactically correct
- * according to the query language indicator of if the query
- * language is not supported.
+ * according to the query language indicator or if the query
+ * language is not supported as specified in {@link #LANGUAGES}.
* @throws org.apache.sling.api.SlingException If an error occurrs querying
* for the resources.
* @throws IllegalStateException if this resource provider has already been
Modified: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceProviderFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceProviderFactory.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceProviderFactory.java (original)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceProviderFactory.java Mon Jul 30 16:20:17 2012
@@ -22,7 +22,12 @@ import java.util.Map;
/**
* The <code>ResourceProviderFactory</code> defines the service API to get and
- * create <code>ResourceProviders</code>s.
+ * create <code>ResourceProviders</code>s dynamically on a per usage base.
+ *
+ * Instead of sharing a resource provider between resource resolvers, the
+ * factory allows to create an own instance of a resource provider per resource
+ * resolver.
+ * The factory also supports authentication.
* <p>
* If the resource provider is not used anymore and implements the {@link DynamicResourceProvider}
* interface, the close method should be called.
@@ -45,8 +50,8 @@ public interface ResourceProviderFactory
* Generally this map will contain a user name and password to authenticate.
* <p>
* If the <code>authenticationInfo</code> map is <code>null</code> the
- * <code>ResourceProvider</code> returned will generally not be authenticated and only provide
- * minimal privileges, if any at all.
+ * <code>ResourceProvider</code> returned will generally not be authenticated
+ * and only provide minimal privileges, if any at all.
*
* @param authenticationInfo
* A map of further credential information which may be used by
Modified: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java (original)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java Mon Jul 30 16:20:17 2012
@@ -367,12 +367,14 @@ public interface ResourceResolver extend
* provided by the query result.
*
* @param query The query string to use to find the resources.
- * @param language The language in which the query is formulated.
+ * @param language The language in which the query is formulated. The
+ * language should always be specified. However for
+ * compatibility with older version, if no language
+ * is specified, "xpath" is used.
* @return An <code>Iterator</code> of {@link Resource} objects matching the
* query.
* @throws QuerySyntaxException If the query is not syntactically correct
- * according to the query language indicator of if the query
- * language is not supported.
+ * according to the query language indicator.
* @throws org.apache.sling.api.SlingException If an error occurrs querying
* for the resources.
* @throws IllegalStateException if this resource resolver has already been
@@ -395,12 +397,14 @@ public interface ResourceResolver extend
* for a value of property type <em>Boolean</em>.
*
* @param query The query string to use to find the resources.
- * @param language The language in which the query is formulated.
+ * @param language The language in which the query is formulated. The
+ * language should always be specified. However for
+ * compatibility with older version, if no language
+ * is specified, "xpath" is used.
* @return An <code>Iterator</code> of <code>Map</code> instances providing
* access to the query result.
* @throws QuerySyntaxException If the query is not syntactically correct
- * according to the query language indicator of if the query
- * language is not supported.
+ * according to the query language indicator.
* @throws org.apache.sling.api.SlingException If an error occurrs querying
* for the resources.
* @throws IllegalStateException if this resource resolver has already been
@@ -506,7 +510,10 @@ public interface ResourceResolver extend
* Delete the resource
* @param resource The resource to delete
*
- * @throws PersistenceException, NullPointerException, UnsupportedOperationException
+ * @throws NullPointerException if the resource parameter is null
+ * @throws UnsupportedOperationException If the resource provider does not allow to
+ * delete this resource.
+ * @throws PersistenceException If the operation fails.
*/
void delete(Resource resource)
throws PersistenceException;
@@ -514,13 +521,17 @@ public interface ResourceResolver extend
/**
* Add a child resource to the given parent resource
* @param parent The parent resource
- * @param name The name of the child resource
+ * @param name The name of the child resource - this is a plain name, not a path!
* @param properties Optional properties for the resource
* @return The new resource
*
- * @throws PersistenceException, NullPointerException, UnsupportedOperationException
+ * @throws NullPointerException if the resource parameter or name parameter is null
+ * @throws IllegalArgumentException if the name contains a slash
+ * @throws UnsupportedOperationException If the resource provider does not allow to
+ * create a resource at that location.
+ * @throws PersistenceException If the operation fails.
*/
- Resource addChild(Resource parent, String name, Map<String, Object> properties)
+ Resource create(Resource parent, String name, Map<String, Object> properties)
throws PersistenceException;
/**
Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/JcrModifiablePropertyMap.java Mon Jul 30 16:20:17 2012
@@ -28,7 +28,6 @@ import javax.jcr.RepositoryException;
import javax.jcr.Value;
import javax.jcr.nodetype.NodeType;
-import org.apache.sling.api.resource.ModifiableValueMap;
import org.apache.sling.api.resource.PersistableValueMap;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.jcr.resource.internal.helper.JcrPropertyMapCacheEntry;
@@ -39,7 +38,7 @@ import org.apache.sling.jcr.resource.int
*/
public final class JcrModifiablePropertyMap
extends JcrPropertyMap
- implements PersistableValueMap, ModifiableValueMap {
+ implements PersistableValueMap {
/** Set of removed and changed properties. */
private Set<String> changedProperties;
@@ -177,29 +176,13 @@ public final class JcrModifiableProperty
/**
* @see org.apache.sling.api.resource.PersistableValueMap#save()
*/
+ @SuppressWarnings("javadoc")
public void save() throws PersistenceException {
if ( this.changedProperties == null || this.changedProperties.size() == 0 ) {
// nothing has changed
return;
}
try {
- this.update();
- getNode().getSession().save();
- } catch (final RepositoryException re) {
- throw new PersistenceException("Unable to persist changes.", re);
- }
- }
-
- /**
- * @see org.apache.sling.api.resource.ModifiableValueMap#update()
- */
- public void update() throws PersistenceException {
- if ( this.changedProperties == null || this.changedProperties.size() == 0 ) {
- // nothing has changed
- return;
- }
-
- try {
final Node node = getNode();
// check for mixin types
if ( this.changedProperties.contains(MIXIN_TYPES) ) {
@@ -227,23 +210,10 @@ public final class JcrModifiableProperty
}
}
}
+ getNode().getSession().save();
this.reset();
} catch (final RepositoryException re) {
throw new PersistenceException("Unable to persist changes.", re, getPath(), null);
}
}
-
- /**
- * @see org.apache.sling.api.resource.ModifiableValueMap#revert()
- */
- public void revert() {
- this.reset();
- }
-
- /**
- * @see org.apache.sling.api.resource.ModifiableValueMap#hasChanges()
- */
- public boolean hasChanges() {
- return this.changedProperties != null && this.changedProperties.size() > 0;
- }
}
Added: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java?rev=1367147&view=auto
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java (added)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java Mon Jul 30 16:20:17 2012
@@ -0,0 +1,670 @@
+/*
+ * 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.resource.internal;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+import javax.jcr.ValueFormatException;
+
+import org.apache.jackrabbit.util.ISO9075;
+import org.apache.jackrabbit.util.Text;
+import org.apache.sling.api.resource.ModifiableValueMap;
+import org.apache.sling.jcr.resource.internal.helper.JcrPropertyMapCacheEntry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This implementation of the value map allows to change
+ * the properies.
+ *
+ * TODO : This adds a lot of duplicate code - we should consolidate.
+ */
+public final class JcrModifiableValueMap
+ implements ModifiableValueMap {
+
+ /** Default logger */
+ private static Logger LOGGER = LoggerFactory.getLogger(JcrModifiableValueMap.class);
+
+ /** The underlying node. */
+ private final Node node;
+
+ /** A cache for the properties. */
+ final Map<String, JcrPropertyMapCacheEntry> cache;
+
+ /** A cache for the values. */
+ final Map<String, Object> valueCache;
+
+ /** Has the node been read completly? */
+ boolean fullyRead;
+
+ private final ClassLoader dynamicClassLoader;
+
+ /**
+ * Constructor
+ * @param node The underlying node.
+ * @param dynamicCL Dynamic class loader for loading serialized objects.
+ */
+ public JcrModifiableValueMap(final Node node, final ClassLoader dynamicCL) {
+ this.node = node;
+ this.cache = new LinkedHashMap<String, JcrPropertyMapCacheEntry>();
+ this.valueCache = new LinkedHashMap<String, Object>();
+ this.fullyRead = false;
+ this.dynamicClassLoader = dynamicCL;
+ }
+
+ /**
+ * Get the node.
+ */
+ private Node getNode() {
+ return node;
+ }
+
+ // ---------- ValueMap
+
+ private String checkKey(final String key) {
+ if ( key == null ) {
+ throw new NullPointerException("Key must not be null.");
+ }
+ if ( key.startsWith("./") ) {
+ return key.substring(2);
+ }
+ return key;
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ValueMap#get(java.lang.String, java.lang.Class)
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T get(final String aKey, final Class<T> type) {
+ final String key = checkKey(aKey);
+ if (type == null) {
+ return (T) get(key);
+ }
+
+ final JcrPropertyMapCacheEntry entry = this.read(key);
+ if ( entry == null ) {
+ return null;
+ }
+ return convertToType(entry, type);
+ }
+
+ /**
+ * @see org.apache.sling.api.resource.ValueMap#get(java.lang.String, java.lang.Object)
+ */
+ @SuppressWarnings("unchecked")
+ public <T> T get(final String aKey,final T defaultValue) {
+ final String key = checkKey(aKey);
+ if (defaultValue == null) {
+ return (T) get(key);
+ }
+
+ // special handling in case the default value implements one
+ // of the interface types supported by the convertToType method
+ Class<T> type = (Class<T>) normalizeClass(defaultValue.getClass());
+
+ T value = get(key, type);
+ if (value == null) {
+ value = defaultValue;
+ }
+
+ return value;
+ }
+
+ // ---------- Map
+
+ /**
+ * @see java.util.Map#get(java.lang.Object)
+ */
+ public Object get(final Object aKey) {
+ final String key = checkKey(aKey.toString());
+ final JcrPropertyMapCacheEntry entry = this.read(key);
+ final Object value = (entry == null ? null : entry.getDefaultValueOrNull());
+ return value;
+ }
+
+ /**
+ * @see java.util.Map#containsKey(java.lang.Object)
+ */
+ public boolean containsKey(final Object key) {
+ return get(key) != null;
+ }
+
+ /**
+ * @see java.util.Map#containsValue(java.lang.Object)
+ */
+ public boolean containsValue(final Object value) {
+ readFully();
+ return valueCache.containsValue(value);
+ }
+
+ /**
+ * @see java.util.Map#isEmpty()
+ */
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ /**
+ * @see java.util.Map#size()
+ */
+ public int size() {
+ readFully();
+ return cache.size();
+ }
+
+ /**
+ * @see java.util.Map#entrySet()
+ */
+ public Set<java.util.Map.Entry<String, Object>> entrySet() {
+ readFully();
+ final Map<String, Object> sourceMap;
+ if (cache.size() == valueCache.size()) {
+ sourceMap = valueCache;
+ } else {
+ sourceMap = transformEntries(cache);
+ }
+ return Collections.unmodifiableSet(sourceMap.entrySet());
+ }
+
+ /**
+ * @see java.util.Map#keySet()
+ */
+ public Set<String> keySet() {
+ readFully();
+ return Collections.unmodifiableSet(cache.keySet());
+ }
+
+ /**
+ * @see java.util.Map#values()
+ */
+ public Collection<Object> values() {
+ readFully();
+ final Map<String, Object> sourceMap;
+ if (cache.size() == valueCache.size()) {
+ sourceMap = valueCache;
+ } else {
+ sourceMap = transformEntries(cache);
+ }
+ return Collections.unmodifiableCollection(sourceMap.values());
+ }
+
+ /**
+ * Return the path of the current node.
+ *
+ * @throws IllegalStateException If a repository exception occurs
+ */
+ public String getPath() {
+ try {
+ return node.getPath();
+ } catch (final RepositoryException e) {
+ throw new IllegalStateException(e);
+ }
+ }
+
+ // ---------- Helpers to access the node's property ------------------------
+
+ /**
+ * Put a single property into the cache
+ * @param prop
+ * @return
+ * @throws IllegalArgumentException if a repository exception occurs
+ */
+ private JcrPropertyMapCacheEntry cacheProperty(final Property prop) {
+ try {
+ // calculate the key
+ final String name = prop.getName();
+ String key = null;
+ if ( name.indexOf("_x") != -1 ) {
+ // for compatiblity with older versions we use the (wrong)
+ // ISO9075 path encoding
+ key = ISO9075.decode(name);
+ if ( key.equals(name) ) {
+ key = null;
+ }
+ }
+ if ( key == null ) {
+ key = Text.unescapeIllegalJcrChars(name);
+ }
+ JcrPropertyMapCacheEntry entry = cache.get(key);
+ if ( entry == null ) {
+ entry = new JcrPropertyMapCacheEntry(prop);
+ cache.put(key, entry);
+
+ final Object defaultValue = entry.getDefaultValue();
+ if (defaultValue != null) {
+ valueCache.put(key, entry.getDefaultValue());
+ }
+ }
+ return entry;
+ } catch (final RepositoryException re) {
+ throw new IllegalArgumentException(re);
+ }
+ }
+
+ /**
+ * Read a single property.
+ * @throws IllegalArgumentException if a repository exception occurs
+ */
+ JcrPropertyMapCacheEntry read(final String name) {
+ // check for empty key
+ if ( name.length() == 0 ) {
+ return null;
+ }
+ // if the name is a path, we should handle this differently
+ if ( name.indexOf('/') != -1 ) {
+ // first a compatibility check with the old (wrong) ISO9075
+ // encoding
+ final String path = ISO9075.encodePath(name);
+ try {
+ if ( node.hasProperty(path) ) {
+ return new JcrPropertyMapCacheEntry(node.getProperty(path));
+ }
+ } catch (final RepositoryException re) {
+ throw new IllegalArgumentException(re);
+ }
+ // now we do a proper segment by segment encoding
+ final StringBuilder sb = new StringBuilder();
+ int pos = 0;
+ int lastPos = -1;
+ while ( pos < name.length() ) {
+ if ( name.charAt(pos) == '/' ) {
+ if ( lastPos + 1 < pos ) {
+ sb.append(Text.escapeIllegalJcrChars(name.substring(lastPos + 1, pos)));
+ }
+ sb.append('/');
+ lastPos = pos;
+ }
+ pos++;
+ }
+ if ( lastPos + 1 < pos ) {
+ sb.append(Text.escapeIllegalJcrChars(name.substring(lastPos + 1)));
+ }
+ final String newPath = sb.toString();
+ try {
+ if ( node.hasProperty(newPath) ) {
+ return new JcrPropertyMapCacheEntry(node.getProperty(newPath));
+ }
+ } catch (final RepositoryException re) {
+ throw new IllegalArgumentException(re);
+ }
+
+ return null;
+ }
+
+ // if the node has been completely read we can directly return
+ if ( fullyRead ) {
+ return cache.get(name);
+ }
+
+ try {
+ final String key = escapeKeyName(name);
+ if (node.hasProperty(key)) {
+ final Property prop = node.getProperty(key);
+ return cacheProperty(prop);
+ }
+ } catch (final RepositoryException re) {
+ throw new IllegalArgumentException(re);
+ }
+
+ try {
+ // for compatiblity with older versions we use the (wrong) ISO9075 path
+ // encoding
+ final String oldKey = ISO9075.encodePath(name);
+ if (node.hasProperty(oldKey)) {
+ final Property prop = node.getProperty(oldKey);
+ return cacheProperty(prop);
+ }
+ } catch (final RepositoryException re) {
+ // we ignore this
+ }
+
+ // property not found
+ return null;
+ }
+
+ /**
+ * Handles key name escaping by taking into consideration if it contains a
+ * registered prefix
+ *
+ * @param key
+ * @return escaped key name
+ */
+ protected String escapeKeyName(final String key) throws RepositoryException {
+ final int indexOfPrefix = key.indexOf(':');
+ // check if colon is neither the first nor the last character
+ if (indexOfPrefix > 0 && key.length() > indexOfPrefix + 1) {
+ final String prefix = key.substring(0, indexOfPrefix);
+ for (final String existingPrefix : getNode().getSession()
+ .getNamespacePrefixes()) {
+ if (existingPrefix.equals(prefix)) {
+ return prefix
+ + ":"
+ + Text.escapeIllegalJcrChars(key
+ .substring(indexOfPrefix + 1));
+ }
+ }
+ }
+ return Text.escapeIllegalJcrChars(key);
+ }
+
+ /**
+ * Read all properties.
+ * @throws IllegalArgumentException if a repository exception occurs
+ */
+ void readFully() {
+ if (!fullyRead) {
+ try {
+ final PropertyIterator pi = node.getProperties();
+ while (pi.hasNext()) {
+ final Property prop = pi.nextProperty();
+ this.cacheProperty(prop);
+ }
+ fullyRead = true;
+ } catch (final RepositoryException re) {
+ throw new IllegalArgumentException(re);
+ }
+ }
+ }
+
+ // ---------- Implementation helper
+
+ @SuppressWarnings("unchecked")
+ private <T> T convertToType(final JcrPropertyMapCacheEntry entry, Class<T> type) {
+ T result = null;
+
+ try {
+ final boolean array = type.isArray();
+
+ if (entry.isMulti) {
+
+ if (array) {
+
+ result = (T) convertToArray(entry,
+ type.getComponentType());
+
+ } else if (entry.values.length > 0) {
+
+ result = convertToType(entry, -1, entry.values[0], type);
+
+ }
+
+ } else {
+
+ if (array) {
+
+ result = (T) convertToArray(entry,
+ type.getComponentType());
+
+ } else {
+
+ result = convertToType(entry, -1, entry.values[0], type);
+
+ }
+ }
+
+ } catch (ValueFormatException vfe) {
+ LOGGER.info("converToType: Cannot convert value of " + entry.getDefaultValueOrNull()
+ + " to " + type, vfe);
+ } catch (RepositoryException re) {
+ LOGGER.info("converToType: Cannot get value of " + entry.getDefaultValueOrNull(), re);
+ }
+
+ // fall back to nothing
+ return result;
+ }
+
+ private <T> T[] convertToArray(final JcrPropertyMapCacheEntry entry, Class<T> type)
+ throws ValueFormatException, RepositoryException {
+ List<T> values = new ArrayList<T>();
+ for (int i = 0; i < entry.values.length; i++) {
+ T value = convertToType(entry, i, entry.values[i], type);
+ if (value != null) {
+ values.add(value);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ T[] result = (T[]) Array.newInstance(type, values.size());
+
+ return values.toArray(result);
+ }
+
+ @SuppressWarnings("unchecked")
+ private <T> T convertToType(final JcrPropertyMapCacheEntry entry,
+ final int index,
+ final Value jcrValue,
+ final Class<T> type)
+ throws ValueFormatException, RepositoryException {
+ Object defaultValue = entry.getDefaultValue();
+ if ( type.isInstance(defaultValue) ) {
+ return (T) defaultValue;
+ }
+
+ if (String.class == type) {
+ return (T) jcrValue.getString();
+
+ } else if (Byte.class == type) {
+ return (T) Byte.valueOf((byte) jcrValue.getLong());
+
+ } else if (Short.class == type) {
+ return (T) Short.valueOf((short) jcrValue.getLong());
+
+ } else if (Integer.class == type) {
+ return (T) Integer.valueOf((int) jcrValue.getLong());
+
+ } else if (Long.class == type) {
+ if (jcrValue.getType() == PropertyType.BINARY) {
+ if (index == -1) {
+ return (T) Long.valueOf(entry.property.getLength());
+ }
+ return (T) Long.valueOf(entry.property.getLengths()[index]);
+ }
+ return (T) Long.valueOf(jcrValue.getLong());
+
+ } else if (Float.class == type) {
+ return (T) Float.valueOf((float) jcrValue.getDouble());
+
+ } else if (Double.class == type) {
+ return (T) Double.valueOf(jcrValue.getDouble());
+
+ } else if (BigDecimal.class == type) {
+ return (T) jcrValue.getDecimal();
+
+ } else if (Boolean.class == type) {
+ return (T) Boolean.valueOf(jcrValue.getBoolean());
+
+ } else if (Date.class == type) {
+ return (T) jcrValue.getDate().getTime();
+
+ } else if (Calendar.class == type) {
+ return (T) jcrValue.getDate();
+
+ } else if (Value.class == type) {
+ return (T) jcrValue;
+
+ } else if (Property.class == type) {
+ return (T) entry.property;
+
+ } else if (Serializable.class.isAssignableFrom(type)
+ && jcrValue.getType() == PropertyType.BINARY) {
+ ObjectInputStream ois = null;
+ try {
+ ois = new ObjectInputStream(jcrValue.getBinary().getStream(), this.dynamicClassLoader);
+ final Object obj = ois.readObject();
+ if ( type.isInstance(obj) ) {
+ return (T)obj;
+ }
+ } catch (ClassNotFoundException cnfe) {
+ // ignore and use fallback
+ } catch (IOException ioe) {
+ // ignore and use fallback
+ } finally {
+ if ( ois != null ) {
+ try {
+ ois.close();
+ } catch (IOException ignore) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ // fallback in case of unsupported type
+ return null;
+ }
+
+ private Class<?> normalizeClass(Class<?> type) {
+ if (Calendar.class.isAssignableFrom(type)) {
+ type = Calendar.class;
+ } else if (Date.class.isAssignableFrom(type)) {
+ type = Date.class;
+ } else if (Value.class.isAssignableFrom(type)) {
+ type = Value.class;
+ } else if (Property.class.isAssignableFrom(type)) {
+ type = Property.class;
+ }
+ return type;
+ }
+
+ private Map<String, Object> transformEntries( Map<String, JcrPropertyMapCacheEntry> map) {
+
+ Map<String, Object> transformedEntries = new LinkedHashMap<String, Object>(map.size());
+ for ( Map.Entry<String, JcrPropertyMapCacheEntry> entry : map.entrySet() )
+ transformedEntries.put(entry.getKey(), entry.getValue().getDefaultValueOrNull());
+
+ return transformedEntries;
+ }
+
+ /**
+ * This is an extended version of the object input stream which uses the
+ * thread context class loader.
+ */
+ private static class ObjectInputStream extends java.io.ObjectInputStream {
+
+ private ClassLoader classloader;
+
+ public ObjectInputStream(final InputStream in, final ClassLoader classLoader) throws IOException {
+ super(in);
+ this.classloader = classLoader;
+ }
+
+ /**
+ * @see java.io.ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
+ */
+ @Override
+ protected Class<?> resolveClass(java.io.ObjectStreamClass classDesc) throws IOException, ClassNotFoundException {
+ if ( this.classloader != null ) {
+ return this.classloader.loadClass(classDesc.getName());
+ }
+ return super.resolveClass(classDesc);
+ }
+ }
+
+ // ---------- Map
+
+ /**
+ * @see java.util.Map#clear()
+ */
+ public void clear() {
+ throw new UnsupportedOperationException("clear");
+ }
+
+ /**
+ * @see java.util.Map#put(java.lang.Object, java.lang.Object)
+ */
+ public Object put(String aKey, Object value) {
+ final String key = checkKey(aKey);
+ if ( key.indexOf('/') != -1 ) {
+ throw new IllegalArgumentException("Invalid key: " + key);
+ }
+ if ( value == null ) {
+ throw new NullPointerException("Value should not be null (key = " + key + ")");
+ }
+ readFully();
+ final Object oldValue = this.get(key);
+ try {
+ final JcrPropertyMapCacheEntry entry = new JcrPropertyMapCacheEntry(value, getNode().getSession());
+ this.cache.put(key, entry);
+ final String name = escapeKeyName(key);
+ if ( entry.isMulti ) {
+ node.setProperty(name, entry.values);
+ } else {
+ node.setProperty(name, entry.values[0]);
+ }
+ } catch (final RepositoryException re) {
+ throw new IllegalArgumentException("Value for key " + key + " can't be put into node: " + value, re);
+ }
+ this.valueCache.put(key, value);
+
+ return oldValue;
+ }
+
+ /**
+ * @see java.util.Map#putAll(java.util.Map)
+ */
+ public void putAll(Map<? extends String, ? extends Object> t) {
+ readFully();
+ if ( t != null ) {
+ final Iterator<?> i = t.entrySet().iterator();
+ while (i.hasNext() ) {
+ @SuppressWarnings("unchecked")
+ final Map.Entry<? extends String, ? extends Object> entry = (Map.Entry<? extends String, ? extends Object>) i.next();
+ put(entry.getKey(), entry.getValue());
+ }
+ }
+ }
+
+ /**
+ * @see java.util.Map#remove(java.lang.Object)
+ */
+ public Object remove(Object aKey) {
+ final String key = checkKey(aKey.toString());
+ readFully();
+ final Object oldValue = this.cache.remove(key);
+ this.valueCache.remove(key);
+ try {
+ final String name = escapeKeyName(key);
+ this.node.setProperty(name, (String)null);
+ } catch (final RepositoryException re) {
+ throw new IllegalArgumentException("Value for key " + key + " can't be removed from node.", re);
+ }
+
+ return oldValue;
+ }
+}
Propchange: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/JcrModifiableValueMap.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrNodeResource.java Mon Jul 30 16:20:17 2012
@@ -50,6 +50,7 @@ import org.apache.sling.api.resource.Val
import org.apache.sling.jcr.resource.JcrModifiablePropertyMap;
import org.apache.sling.jcr.resource.JcrPropertyMap;
import org.apache.sling.jcr.resource.JcrResourceConstants;
+import org.apache.sling.jcr.resource.internal.JcrModifiableValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -127,7 +128,7 @@ public class JcrNodeResource extends Jcr
return (Type) getURL(); // unchecked cast
} else if (type == Map.class || type == ValueMap.class) {
return (Type) new JcrPropertyMap(getNode(), this.dynamicClassLoader); // unchecked cast
- } else if (type == PersistableValueMap.class || type == ModifiableValueMap.class ) {
+ } else if (type == PersistableValueMap.class ) {
// check write
try {
getNode().getSession().checkPermission(getNode().getPath(),
@@ -136,6 +137,23 @@ public class JcrNodeResource extends Jcr
} catch (AccessControlException ace) {
// the user has no write permission, cannot adapt
LOGGER.debug(
+ "adaptTo(PersistableValueMap): Cannot set properties on {}",
+ this);
+ } catch (RepositoryException e) {
+ // some other problem, cannot adapt
+ LOGGER.debug(
+ "adaptTo(PersistableValueMap): Unexpected problem for {}",
+ this);
+ }
+ } else if (type == ModifiableValueMap.class ) {
+ // check write
+ try {
+ getNode().getSession().checkPermission(getNode().getPath(),
+ "set_property");
+ return (Type) new JcrModifiableValueMap(getNode(), this.dynamicClassLoader);
+ } catch (AccessControlException ace) {
+ // the user has no write permission, cannot adapt
+ LOGGER.debug(
"adaptTo(ModifiableValueMap): Cannot set properties on {}",
this);
} catch (RepositoryException e) {
Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProvider.java Mon Jul 30 16:20:17 2012
@@ -49,8 +49,8 @@ import org.apache.sling.api.resource.Res
import org.apache.sling.api.resource.ResourceProvider;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
-import org.apache.sling.jcr.resource.JcrModifiablePropertyMap;
import org.apache.sling.jcr.resource.JcrResourceUtil;
+import org.apache.sling.jcr.resource.internal.JcrModifiableValueMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -391,13 +391,16 @@ public class JcrResourceProvider
if ( properties != null ) {
// create modifiable map
- final JcrModifiablePropertyMap jcrMap = new JcrModifiablePropertyMap(node, this.dynamicClassLoader);
+ final JcrModifiableValueMap jcrMap = new JcrModifiableValueMap(node, this.dynamicClassLoader);
for(final Map.Entry<String, Object> entry : properties.entrySet()) {
if ( !"jcr:primaryType".equals(entry.getKey()) ) {
- jcrMap.put(entry.getKey(), entry.getValue());
+ try {
+ jcrMap.put(entry.getKey(), entry.getValue());
+ } catch (final IllegalArgumentException iae) {
+ throw new PersistenceException(iae.getMessage(), iae, path, entry.getKey());
+ }
}
}
- jcrMap.update();
}
return new JcrNodeResource(resolver, node, this.dynamicClassLoader);
Modified: sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderFactory.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderFactory.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderFactory.java (original)
+++ sling/trunk/bundles/jcr/resource/src/main/java/org/apache/sling/jcr/resource/internal/helper/jcr/JcrResourceProviderFactory.java Mon Jul 30 16:20:17 2012
@@ -25,6 +25,7 @@ import javax.jcr.Credentials;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.SimpleCredentials;
+import javax.jcr.query.Query;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
@@ -34,6 +35,7 @@ import org.apache.felix.scr.annotations.
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.QueriableResourceProvider;
import org.apache.sling.api.resource.ResourceProvider;
import org.apache.sling.api.resource.ResourceProviderFactory;
import org.apache.sling.api.resource.ResourceResolver;
@@ -53,7 +55,8 @@ import org.osgi.framework.Constants;
@Property(name=ResourceProviderFactory.PROPERTY_REQUIRED, boolValue=true),
@Property(name=ResourceProvider.ROOTS, value="/"),
@Property(name = Constants.SERVICE_DESCRIPTION, value = "Apache Sling JCR Resource Provider Factory"),
- @Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software Foundation")
+ @Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software Foundation"),
+ @Property(name = QueriableResourceProvider.LANGUAGES, value = {Query.XPATH, Query.SQL, Query.JCR_SQL2})
})
public class JcrResourceProviderFactory implements ResourceProviderFactory {
Modified: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java (original)
+++ sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/JcrResourceListenerTest.java Mon Jul 30 16:20:17 2012
@@ -206,7 +206,7 @@ public class JcrResourceListenerTest ext
// TODO Auto-generated method stub
}
- public Resource addChild(Resource parent, String name, Map<String, Object> properties) {
+ public Resource create(Resource parent, String name, Map<String, Object> properties) {
// TODO Auto-generated method stub
return null;
}
Modified: sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java (original)
+++ sling/trunk/bundles/jcr/resource/src/test/java/org/apache/sling/jcr/resource/internal/helper/jcr/MockResourceResolver.java Mon Jul 30 16:20:17 2012
@@ -130,7 +130,7 @@ public class MockResourceResolver implem
// TODO Auto-generated method stub
}
- public Resource addChild(Resource parent, String name, Map<String, Object> properties) {
+ public Resource create(Resource parent, String name, Map<String, Object> properties) {
// TODO Auto-generated method stub
return null;
}
Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java Mon Jul 30 16:20:17 2012
@@ -618,6 +618,8 @@ public class ResourceResolverImpl extend
// ---------- Querying resources
+ private static final String DEFAULT_QUERY_LANGUAGE = "xpath";
+
/**
* @see org.apache.sling.api.resource.ResourceResolver#findResources(java.lang.String,
* java.lang.String)
@@ -626,7 +628,8 @@ public class ResourceResolverImpl extend
checkClosed();
return new ResourceIteratorDecorator(this.factory.getResourceDecoratorTracker(),
- this.factory.getRootProviderEntry().findResources(this.context, this, query, language));
+ this.factory.getRootProviderEntry().findResources(this.context, this, query,
+ language == null ? DEFAULT_QUERY_LANGUAGE : language));
}
/**
@@ -637,7 +640,8 @@ public class ResourceResolverImpl extend
throws SlingException {
checkClosed();
- return this.factory.getRootProviderEntry().queryResources(this.context, query, language);
+ return this.factory.getRootProviderEntry().queryResources(this.context, query,
+ language == null ? DEFAULT_QUERY_LANGUAGE : language);
}
/**
@@ -1014,9 +1018,9 @@ public class ResourceResolverImpl extend
}
/**
- * @see org.apache.sling.api.resource.ResourceResolver#addChild(org.apache.sling.api.resource.Resource, java.lang.String, Map)
+ * @see org.apache.sling.api.resource.ResourceResolver#create(org.apache.sling.api.resource.Resource, java.lang.String, Map)
*/
- public Resource addChild(final Resource parent, final String name, final Map<String, Object> properties)
+ public Resource create(final Resource parent, final String name, final Map<String, Object> properties)
throws PersistenceException {
// if parent or name is null, we get an NPE as stated in the API
if ( name == null ) {
@@ -1024,7 +1028,7 @@ public class ResourceResolverImpl extend
}
// name should be a name not a path
if ( name.indexOf("/") != -1 ) {
- throw new PersistenceException("Name should not contain a slash: " + name, null, parent.getPath(), null);
+ throw new IllegalArgumentException("Name should not contain a slash: " + name);
}
final String path;
if ( parent.getPath().equals("/") ) {
Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderList.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderList.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderList.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderList.java Mon Jul 30 16:20:17 2012
@@ -25,6 +25,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
+import org.apache.sling.resourceresolver.impl.tree.ProviderHandler;
import org.apache.sling.resourceresolver.impl.tree.ResourceProviderFactoryHandler;
import org.apache.sling.resourceresolver.impl.tree.ResourceProviderHandler;
@@ -32,7 +33,6 @@ import org.apache.sling.resourceresolver
* Helper class to get a sorted list of resource providers which implement
* a specific feature interface.
*/
-//TODO - Implement sorting
public class SortedProviderList<T> {
/** The feature interface class. */
@@ -49,11 +49,16 @@ public class SortedProviderList<T> {
this.genericClass = genericClass;
}
+ public interface Filter<T> {
+
+ boolean select(ProviderHandler handler, T provider);
+ }
+
/**
* Return an iterator for the current sorted list of providers
* implementing the feature interface.
*/
- public Iterator<T> getProviders(final ResourceResolverContext ctx) {
+ public Iterator<T> getProviders(final ResourceResolverContext ctx, final Filter<T> filter) {
return new Iterator<T>() {
private final Entry[] list = sortedList;
@@ -65,16 +70,20 @@ public class SortedProviderList<T> {
private Object seek() {
Object result;
if ( this.index < list.length ) {
- result = list[this.index].object;
+ final Entry entry = list[this.index];
+ result = entry.object;
this.index++;
if ( result instanceof ResourceProviderFactoryHandler ) {
result = ((ResourceProviderFactoryHandler)result).getResourceProvider(ctx);
if ( !genericClass.isAssignableFrom(result.getClass())) {
result = null;
}
- if ( result == null ) {
- result = seek();
- }
+ }
+ if ( result != null && filter != null && !filter.select(entry.handler, (T)result)) {
+ result = null;
+ }
+ if ( result == null ) {
+ result = seek();
}
} else {
result = null;
@@ -115,11 +124,10 @@ public class SortedProviderList<T> {
* Add an object to the list
*/
private synchronized void addToList(final Object obj,
- final String[] roots,
- final Long serviceId) {
+ final ProviderHandler handler) {
final List<Entry> list = new ArrayList<Entry>();
list.addAll(Arrays.asList(this.sortedList));
- list.add(new Entry(obj, roots, serviceId));
+ list.add(new Entry(obj, handler));
Collections.sort(list);
this.sortedList = list.toArray(new Entry[list.size()]);
}
@@ -147,7 +155,7 @@ public class SortedProviderList<T> {
*/
public void add(final ResourceProviderHandler rpHandler) {
if ( genericClass.isAssignableFrom(rpHandler.getResourceProvider().getClass())) {
- this.addToList(rpHandler.getResourceProvider(), rpHandler.getRoots(), rpHandler.getServiceId());
+ this.addToList(rpHandler.getResourceProvider(), rpHandler);
}
}
@@ -157,7 +165,7 @@ public class SortedProviderList<T> {
* This will be checked on demand.
*/
public void add(final ResourceProviderFactoryHandler factory) {
- this.addToList(factory, factory.getRoots(), factory.getServiceId());
+ this.addToList(factory, factory);
}
/**
@@ -179,18 +187,20 @@ public class SortedProviderList<T> {
private static final class Entry implements Comparable<Entry> {
private final String path;
- private final Long id;
+
+ public final ProviderHandler handler;
public final Object object;
- public Entry(final Object object, final String[] roots, final Long serviceId) {
- this.id = serviceId;
+ public Entry(final Object object,
+ final ProviderHandler handler) {
this.object = object;
- if ( roots != null ) {
- this.path = roots[0];
+ if ( handler.getRoots() != null ) {
+ this.path = handler.getRoots()[0];
} else {
this.path = "";
}
+ this.handler = handler;
}
/**
@@ -199,10 +209,9 @@ public class SortedProviderList<T> {
public int compareTo(final Entry other) {
int result = this.path.compareTo(other.path);
if ( result == 0 ) {
- result = this.id.compareTo(other.id);
+ result = this.handler.getServiceId().compareTo(other.handler.getServiceId());
}
return result;
}
-
}
}
Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java Mon Jul 30 16:20:17 2012
@@ -19,10 +19,13 @@ package org.apache.sling.resourceresolve
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import org.apache.sling.api.resource.QueriableResourceProvider;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceProvider;
import org.apache.sling.api.resource.ResourceResolver;
@@ -46,6 +49,9 @@ public abstract class ProviderHandler im
/** Configured roots. */
private final String[] roots;
+ /** Configured languages for queries. */
+ private final Set<String> queryLanguages;
+
/** Owns roots? */
private final boolean ownsRoots;
@@ -79,6 +85,23 @@ public abstract class ProviderHandler im
this.roots = configuredRoots.toArray(new String[configuredRoots.size()]);
}
this.ownsRoots = PropertiesUtil.toBoolean(properties.get(ResourceProvider.OWNS_ROOTS), false);
+ final Set<String> configuredLanguages = new HashSet<String>();
+ final String[] languages = PropertiesUtil.toStringArray(properties.get(QueriableResourceProvider.LANGUAGES));
+ if ( languages != null) {
+ for(final String l : languages) {
+ if (l != null) {
+ String language = l.trim();
+ if ( language.length() > 0 ) {
+ configuredLanguages.add(language);
+ }
+ }
+ }
+ }
+ if ( configuredLanguages.size() == 0 ) {
+ this.queryLanguages = null;
+ } else {
+ this.queryLanguages = configuredLanguages;
+ }
}
/**
@@ -112,6 +135,13 @@ public abstract class ProviderHandler im
}
/**
+ * Check if the resource provider supports the language.
+ */
+ public boolean supportsQueryLanguages(final String language) {
+ return this.queryLanguages != null && this.queryLanguages.contains(language);
+ }
+
+ /**
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(final ProviderHandler other) {
Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/RootResourceProviderEntry.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/RootResourceProviderEntry.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/RootResourceProviderEntry.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/tree/RootResourceProviderEntry.java Mon Jul 30 16:20:17 2012
@@ -105,7 +105,7 @@ public class RootResourceProviderEntry e
* @see Adaptable
*/
public <AdapterType> AdapterType adaptTo(final ResourceResolverContext ctx, final Class<AdapterType> type) {
- final Iterator<Adaptable> i = this.adaptableProviders.getProviders(ctx);
+ final Iterator<Adaptable> i = this.adaptableProviders.getProviders(ctx, null);
AdapterType result = null;
while ( result == null && i.hasNext() ) {
final Adaptable adap = i.next();
@@ -120,7 +120,14 @@ public class RootResourceProviderEntry e
*/
public Iterator<Resource> findResources(final ResourceResolverContext ctx,
final ResourceResolver resolver, final String query, final String language) {
- final Iterator<QueriableResourceProvider> i = this.queriableProviders.getProviders(ctx);
+ final Iterator<QueriableResourceProvider> i = this.queriableProviders.getProviders(ctx,
+ new SortedProviderList.Filter<QueriableResourceProvider>() {
+
+ public boolean select(final ProviderHandler handler, final QueriableResourceProvider provider) {
+ return handler.supportsQueryLanguages(language);
+ }
+
+ });
return new Iterator<Resource>() {
private Resource nextObject = this.seek();
@@ -180,7 +187,14 @@ public class RootResourceProviderEntry e
* @see QueriableResourceProvider#queryResources(String, String)
*/
public Iterator<Map<String, Object>> queryResources(final ResourceResolverContext ctx, final String query, final String language) {
- final Iterator<QueriableResourceProvider> i = this.queriableProviders.getProviders(ctx);
+ final Iterator<QueriableResourceProvider> i = this.queriableProviders.getProviders(ctx,
+ new SortedProviderList.Filter<QueriableResourceProvider>() {
+
+ public boolean select(final ProviderHandler handler, final QueriableResourceProvider provider) {
+ return handler.supportsQueryLanguages(language);
+ }
+
+ });
return new Iterator<Map<String, Object>>() {
private Map<String, Object> nextObject = this.seek();
@@ -246,7 +260,7 @@ public class RootResourceProviderEntry e
if ( ctx.getAuthenticationInfo() != null ) {
names.addAll(ctx.getAuthenticationInfo().keySet());
}
- final Iterator<AttributableResourceProvider> i = this.attributableProviders.getProviders(ctx);
+ final Iterator<AttributableResourceProvider> i = this.attributableProviders.getProviders(ctx, null);
while ( i.hasNext() ) {
final AttributableResourceProvider adap = i.next();
final Collection<String> newNames = adap.getAttributeNames();
@@ -270,7 +284,7 @@ public class RootResourceProviderEntry e
result = ctx.getAuthenticationInfo().get(name);
}
if ( result == null ) {
- final Iterator<AttributableResourceProvider> i = this.attributableProviders.getProviders(ctx);
+ final Iterator<AttributableResourceProvider> i = this.attributableProviders.getProviders(ctx, null);
while ( result == null && i.hasNext() ) {
final AttributableResourceProvider adap = i.next();
result = adap.getAttribute(name);
Modified: sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java (original)
+++ sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java Mon Jul 30 16:20:17 2012
@@ -43,7 +43,6 @@ import javax.servlet.http.HttpSession;
import org.apache.sling.api.resource.AbstractResource;
import org.apache.sling.api.resource.NonExistingResource;
-import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceMetadata;
import org.apache.sling.api.resource.ResourceResolver;
@@ -305,25 +304,25 @@ public class ResourceResolverImplTest {
@Test public void testBasicCrud() throws Exception {
try {
- this.resResolver.addChild(null, "a", null);
+ this.resResolver.create(null, "a", null);
fail("Null parent resource should throw NPE");
} catch (final NullPointerException npe) {
// correct
}
try {
- this.resResolver.addChild(new ResourceImpl(), null, null);
+ this.resResolver.create(new ResourceImpl(), null, null);
fail("Null name should throw NPE");
} catch (final NullPointerException npe) {
// correct
}
try {
- this.resResolver.addChild(new ResourceImpl(), "a/b", null);
- fail("Slash in name should throw persistence exception");
- } catch (final PersistenceException pe) {
+ this.resResolver.create(new ResourceImpl(), "a/b", null);
+ fail("Slash in name should throw illegal argument exception");
+ } catch (final IllegalArgumentException pe) {
// correct
}
try {
- this.resResolver.addChild(new ResourceImpl(), "a", null);
+ this.resResolver.create(new ResourceImpl(), "a", null);
fail("This should be unsupported.");
} catch (final UnsupportedOperationException uoe) {
// correct
Modified: sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderListTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderListTest.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderListTest.java (original)
+++ sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderListTest.java Mon Jul 30 16:20:17 2012
@@ -153,7 +153,7 @@ public class SortedProviderListTest {
final SortedProviderList<Adaptable> spl = new SortedProviderList<Adaptable>(Adaptable.class);
spl.add(new ResourceProviderHandler(rp2, rp2.getProperties()));
- final Iterator<Adaptable> i = spl.getProviders(null);
+ final Iterator<Adaptable> i = spl.getProviders(null, null);
assertTrue(i.hasNext());
i.next(); // one entry
assertFalse(i.hasNext());
@@ -179,7 +179,7 @@ public class SortedProviderListTest {
final ResourceResolverContext ctx,
final Adaptable... objects) {
final int expectedCount = objects == null ? 0 : objects.length;
- final Iterator<Adaptable> i = spl.getProviders(ctx);
+ final Iterator<Adaptable> i = spl.getProviders(ctx, null);
int count = 0;
while ( i.hasNext() ) {
final Adaptable a = i.next();
Modified: sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java (original)
+++ sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java Mon Jul 30 16:20:17 2012
@@ -303,7 +303,7 @@ public class ResourceProviderEntryTest {
// TODO Auto-generated method stub
}
- public Resource addChild(Resource parent, String name, Map<String, Object> properties) {
+ public Resource create(Resource parent, String name, Map<String, Object> properties) {
// TODO Auto-generated method stub
return null;
}
Modified: sling/trunk/launchpad/integration-tests/src/main/resources/integration-test/crud/crud-test.jsp
URL: http://svn.apache.org/viewvc/sling/trunk/launchpad/integration-tests/src/main/resources/integration-test/crud/crud-test.jsp?rev=1367147&r1=1367146&r2=1367147&view=diff
==============================================================================
--- sling/trunk/launchpad/integration-tests/src/main/resources/integration-test/crud/crud-test.jsp (original)
+++ sling/trunk/launchpad/integration-tests/src/main/resources/integration-test/crud/crud-test.jsp Mon Jul 30 16:20:17 2012
@@ -36,7 +36,7 @@
vm.put("a", request.getParameter("a"));
vm.put("b", request.getParameter("b"));
- final Resource rsrc = resolver.addChild(resolver.getResource("/"), name, vm);
+ final Resource rsrc = resolver.create(resolver.getResource("/"), name, vm);
resolver.commit();
%>Resource created at <%= rsrc.getPath() %><%