[exo-jcr-commits] exo-jcr SVN: r2761 - in ws/trunk: exo.ws.rest.core/src/main/java/org/exoplatform/services/rest and 10 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Fri Jul 9 09:37:31 EDT 2010


Author: aparfonov
Date: 2010-07-09 09:37:30 -0400 (Fri, 09 Jul 2010)
New Revision: 2761

Added:
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ResourcePublicationException.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/tools/DummySecurityContext.java
Modified:
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/ObjectModel.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ConstructorDescriptorImpl.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/FilterDescriptorImpl.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/RequestDispatcher.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/RequestHandlerImpl.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ResourceBinder.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/method/DefaultMethodInvoker.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/provider/ProviderDescriptorImpl.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/resource/AbstractResourceDescriptorImpl.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/resource/PathValue.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/resource/AbstractResourceDescriptor.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/tools/ResourceLauncher.java
   ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/uri/UriPattern.java
   ws/trunk/exo.ws.rest.core/src/test/java/org/exoplatform/services/rest/BaseTest.java
   ws/trunk/exo.ws.rest.core/src/test/java/org/exoplatform/services/rest/impl/ResourceBinderTest.java
   ws/trunk/exo.ws.rest.core/src/test/java/org/exoplatform/services/rest/uri/UriPatternTest.java
   ws/trunk/pom.xml
Log:
EXOJCR-823: 

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/ObjectModel.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/ObjectModel.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/ObjectModel.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -20,9 +20,11 @@
 
 import java.util.List;
 
+import javax.ws.rs.core.MultivaluedMap;
+
 /**
  * Abstract description of object.
- * 
+ *
  * @author <a href="mailto:andrew00x at gmail.com">Andrey Parfonov</a>
  * @version $Id: $
  */
@@ -30,7 +32,7 @@
 {
 
    /**
-    * @return collections constructor, MAY return empty collection or null if
+    * @return collections constructor, return empty collection not null if
     *         object is singleton. There is no setter for this to add new
     *         ConstructorInjector use
     *         <code>ObjectModel.getConstructorDescriptors().add(ConstructorInjector)</code>
@@ -38,8 +40,8 @@
    List<ConstructorDescriptor> getConstructorDescriptors();
 
    /**
-    * @return collections of object fields, MAY return empty collection or null
-    *         if object is singleton. There is no setter for this to add new
+    * @return collections of object fields, return empty collection not null if
+    *         object is singleton. There is no setter for this to add new
     *         ConstructorInjector use
     *         <code>ObjectModel.getFieldInjectors().add(FieldInjector)</code>
     */
@@ -50,4 +52,19 @@
     */
    Class<?> getObjectClass();
 
+   /**
+    * @param key
+    * @return property by key
+    * @see #getProperties()
+    */
+   List<String> getProperty(String key);
+
+   /**
+    * Optional attributes.
+    *
+    * @return all properties. If there is no any optional attributes then empty
+    *         map returned never <code>null</code>
+    */
+   MultivaluedMap<String, String> getProperties();
+
 }

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ConstructorDescriptorImpl.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ConstructorDescriptorImpl.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ConstructorDescriptorImpl.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -233,11 +233,14 @@
             }
             catch (Exception e)
             {
+               String msg = "Not able resolve constructor parameter " + cp;
                Class<?> ac = a.annotationType();
                if (ac == MatrixParam.class || ac == QueryParam.class || ac == PathParam.class)
-                  throw new WebApplicationException(e, Response.status(Response.Status.NOT_FOUND).build());
+                  throw new WebApplicationException(e, Response.status(Response.Status.NOT_FOUND).entity(msg).type(
+                     MediaType.TEXT_PLAIN).build());
 
-               throw new WebApplicationException(e, Response.status(Response.Status.BAD_REQUEST).build());
+               throw new WebApplicationException(e, Response.status(Response.Status.BAD_REQUEST).entity(msg).type(
+                  MediaType.TEXT_PLAIN).build());
             }
          }
          else

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/FilterDescriptorImpl.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/FilterDescriptorImpl.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/FilterDescriptorImpl.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -32,6 +32,7 @@
 import java.util.List;
 
 import javax.ws.rs.Path;
+import javax.ws.rs.core.MultivaluedMap;
 
 /**
  * @author <a href="mailto:andrew00x at gmail.com">Andrey Parfonov</a>
@@ -57,7 +58,7 @@
 
    /**
     * Filter class constructors.
-    * 
+    *
     * @see ConstructorDescriptor
     */
    private final List<ConstructorDescriptor> constructors;
@@ -67,6 +68,9 @@
     */
    private final List<FieldInjector> fields;
 
+   /** Optional data. */
+   private MultivaluedMap<String, String> properties;
+
    /**
     * @param filterClass {@link Class} of filter
     */
@@ -173,6 +177,30 @@
    /**
     * {@inheritDoc}
     */
+   public MultivaluedMap<String, String> getProperties()
+   {
+      if (properties == null)
+      {
+         properties = new MultivaluedMapImpl();
+      }
+      return properties;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public List<String> getProperty(String key)
+   {
+      if (properties != null)
+      {
+         return properties.get(key);
+      }
+      return null;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
    public UriPattern getUriPattern()
    {
       return uriPattern;

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/RequestDispatcher.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/RequestDispatcher.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/RequestDispatcher.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -62,14 +62,10 @@
 public class RequestDispatcher
 {
 
-   /**
-    * Logger.
-    */
+   /** Logger. */
    private static final Log LOG = ExoLogger.getLogger("exo.ws.rest.core.RequestDispatcher");
 
-   /**
-    * See {@link ResourceBinder}.
-    */
+   /** See {@link ResourceBinder}. */
    protected final ResourceBinder resourceBinder;
 
    private final MethodInvokerFactory invokerFactory;
@@ -370,7 +366,6 @@
       if (returnType == void.class || o == null)
       {
          response.setResponse(Response.noContent().build());
-
       }
       else if (Response.class.isAssignableFrom(returnType))
       {
@@ -380,19 +375,14 @@
          {
             r.getMetadata().putSingle(HttpHeaders.CONTENT_TYPE, contentType);
          }
-
          response.setResponse(r);
-
       }
       else if (GenericEntity.class.isAssignableFrom(returnType))
       {
-
          response.setResponse(Response.ok(o, contentType).build());
-
       }
       else
       {
-
          response.setResponse(Response.ok(o, contentType).build());
       }
 
@@ -565,60 +555,28 @@
    }
 
    /**
-    * Get root resource
+    * Get root resource.
     *
     * @param parameterValues is taken from context
     * @param requestPath is taken from context
     * @return root resource
+    * @throws WebApplicationException if there is no matched root resources.
+    *         Exception with prepared error response with 'Not Found' status
     */
    protected ObjectFactory<AbstractResourceDescriptor> getRootResourse(List<String> parameterValues, String requestPath)
    {
-      ObjectFactory<AbstractResourceDescriptor> resourceFactory = null;
-      List<ObjectFactory<AbstractResourceDescriptor>> resources = resourceBinder.getResources();
-      // be sure no new entries added
-      synchronized (resources)
-      {
-         for (ObjectFactory<AbstractResourceDescriptor> rc : resources)
-         {
-            if (rc.getObjectModel().getUriPattern().match(requestPath, parameterValues))
-            {
-               // all times will at least 1
-               int len = parameterValues.size();
-               // If capturing group contains last element and this element is
-               // neither null nor '/' then ResourceClass must contains at least one
-               // sub-resource method or sub-resource locator.
-               if (parameterValues.get(len - 1) != null && !parameterValues.get(len - 1).equals("/"))
-               {
-                  int subresnum =
-                     rc.getObjectModel().getSubResourceMethods().size()
-                        + rc.getObjectModel().getSubResourceLocators().size();
-                  if (subresnum == 0)
-                  {
-                     continue;
-                  }
-               }
-               resourceFactory = rc;
-               break;
-            }
-         }
-
-      }
-
+      ObjectFactory<AbstractResourceDescriptor> resourceFactory =
+         resourceBinder.getMatchedResource(requestPath, parameterValues);
       if (resourceFactory == null)
       {
-
          if (LOG.isDebugEnabled())
          {
             LOG.debug("Root resource not found for " + requestPath);
          }
-
          // Stop here, there is no matched root resource
          throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).entity(
             "There is no any resources matched to request path " + requestPath).type(MediaType.TEXT_PLAIN).build());
       }
-      else
-      {
-         return resourceFactory;
-      }
+      return resourceFactory;
    }
 }

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/RequestHandlerImpl.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/RequestHandlerImpl.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/RequestHandlerImpl.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -18,21 +18,6 @@
  */
 package org.exoplatform.services.rest.impl;
 
-import java.io.File;
-import java.io.OutputStream;
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-import javax.ws.rs.WebApplicationException;
-import javax.ws.rs.core.MediaType;
-import javax.ws.rs.core.Response;
-import javax.ws.rs.core.StreamingOutput;
-import javax.ws.rs.core.Response.ResponseBuilder;
-import javax.ws.rs.ext.ExceptionMapper;
-
 import org.exoplatform.container.component.ComponentPlugin;
 import org.exoplatform.container.xml.InitParams;
 import org.exoplatform.container.xml.ValueParam;
@@ -53,6 +38,21 @@
 import org.exoplatform.services.rest.provider.EntityProvider;
 import org.picocontainer.Startable;
 
+import java.io.File;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.StreamingOutput;
+import javax.ws.rs.core.Response.ResponseBuilder;
+import javax.ws.rs.ext.ExceptionMapper;
+
 /**
  * @author <a href="mailto:andrew00x at gmail.com">Andrey Parfonov</a>
  * @version $Id: $
@@ -67,7 +67,7 @@
 
    /**
     * Application properties. Properties from this map will be copied to ApplicationContext
-    * and may be accessible via method {@link ApplicationContextImpl#getProperties()}. 
+    * and may be accessible via method {@link ApplicationContextImpl#getProperties()}.
     */
    private static final Map<String, String> properties = new HashMap<String, String>();
 
@@ -90,7 +90,7 @@
 
    /**
     * Constructs new instance of {@link RequestHandler}.
-    * 
+    *
     * @param dispatcher See {@link RequestDispatcher}
     * @param params init parameters
     */
@@ -248,7 +248,7 @@
 
    /**
     * Create error response with specified status and body message.
-    *  
+    *
     * @param status response status
     * @param message response message
     * @return response
@@ -267,7 +267,7 @@
 
    /**
     * Get JAXR header for response status.
-    * 
+    *
     * @param status response status
     * @return JAXRS header or null.
     */
@@ -372,7 +372,7 @@
 
    /**
     * Processing {@link ComponentPlugin} for injection external components.
-    * 
+    *
     * @param plugin See {@link ComponentPlugin}
     */
    @SuppressWarnings("unchecked")

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ResourceBinder.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ResourceBinder.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ResourceBinder.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -22,12 +22,15 @@
 import org.exoplatform.container.ExoContainerContext;
 import org.exoplatform.services.log.ExoLogger;
 import org.exoplatform.services.log.Log;
+import org.exoplatform.services.rest.ApplicationContext;
 import org.exoplatform.services.rest.Filter;
 import org.exoplatform.services.rest.ObjectFactory;
+import org.exoplatform.services.rest.ObjectModel;
 import org.exoplatform.services.rest.PerRequestObjectFactory;
 import org.exoplatform.services.rest.RequestFilter;
 import org.exoplatform.services.rest.ResponseFilter;
 import org.exoplatform.services.rest.SingletonObjectFactory;
+import org.exoplatform.services.rest.impl.method.DefaultMethodInvoker;
 import org.exoplatform.services.rest.impl.method.MethodInvokerFactory;
 import org.exoplatform.services.rest.impl.resource.AbstractResourceDescriptorImpl;
 import org.exoplatform.services.rest.impl.resource.ResourceDescriptorValidator;
@@ -45,6 +48,7 @@
 
 import javax.ws.rs.Path;
 import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.ext.ContextResolver;
 import javax.ws.rs.ext.ExceptionMapper;
 import javax.ws.rs.ext.MessageBodyReader;
@@ -64,60 +68,60 @@
 public class ResourceBinder
 {
 
-   /**
-    * Logger.
-    */
+   /** Logger. */
    private static final Log LOG = ExoLogger.getLogger("exo.ws.rest.core.ResourceBinder");
 
+   /** Resource's comparator. */
    protected static final Comparator<ObjectFactory<AbstractResourceDescriptor>> RESOURCE_COMPARATOR =
-      new ResourceComparator();
-
-   /**
-    * Compare two {@link SingletonResourceFactory}.
-    */
-   private static final class ResourceComparator implements Comparator<ObjectFactory<AbstractResourceDescriptor>>
-   {
-      /**
-       * Compare two ResourceClass for order.
-       *
-       * @param o1 first ResourceClass to be compared
-       * @param o2 second ResourceClass to be compared
-       * @return positive , zero or negative dependent of {@link UriPattern}
-       *         comparison
-       * @see Comparator#compare(Object, Object)
-       * @see UriPattern
-       * @see UriPattern#URIPATTERN_COMPARATOR
-       */
-      public int compare(ObjectFactory<AbstractResourceDescriptor> o1, ObjectFactory<AbstractResourceDescriptor> o2)
+      new Comparator<ObjectFactory<AbstractResourceDescriptor>>()
       {
-         return UriPattern.URIPATTERN_COMPARATOR.compare(o1.getObjectModel().getUriPattern(), o2.getObjectModel()
-            .getUriPattern());
-      }
-   };
+         /**
+          * Compare two ResourceClass for order.
+          *
+          * @param o1 first ResourceClass to be compared
+          * @param o2 second ResourceClass to be compared
+          * @return positive , zero or negative dependent of {@link UriPattern}
+          *         comparison
+          * @see Comparator#compare(Object, Object)
+          * @see UriPattern
+          * @see UriPattern#URIPATTERN_COMPARATOR
+          */
+         public int compare(ObjectFactory<AbstractResourceDescriptor> o1, ObjectFactory<AbstractResourceDescriptor> o2)
+         {
+            return UriPattern.URIPATTERN_COMPARATOR.compare(o1.getObjectModel().getUriPattern(), o2.getObjectModel()
+               .getUriPattern());
+         }
+      };
 
-   /**
-    * Root resource descriptors.
-    */
-   protected final List<ObjectFactory<AbstractResourceDescriptor>> rootResources =
-      new ArrayList<ObjectFactory<AbstractResourceDescriptor>>();
-
-   /**
-    * Validator.
-    */
+   /** Validator. */
    protected final ResourceDescriptorVisitor rdv = ResourceDescriptorValidator.getInstance();
 
+   /** Amount of available root resources. */
    protected int size = 0;
 
+   /** @see RuntimeDelegate */
+   protected final RuntimeDelegate rd;
+
    /**
-    * @see RuntimeDelegate
+    * Producer of methods invokers. If not specified then
+    * {@link DefaultMethodInvoker} will be in use.
     */
-   protected final RuntimeDelegate rd;
-
    protected final MethodInvokerFactory invokerFactory;
 
+   /** List of all available root resources. */
+   protected final List<ObjectFactory<AbstractResourceDescriptor>> rootResources =
+      new ArrayList<ObjectFactory<AbstractResourceDescriptor>>();
+
+   public ResourceBinder(ExoContainerContext containerContext) throws Exception
+   {
+      this(containerContext, null);
+   }
+
    /**
     * @param containerContext eXo container context
+    * @param invokerFactory method invoker producer
     * @throws Exception if can't set instance of {@link RuntimeDelegate}
+    * @see MethodInvokerFactory
     */
    @SuppressWarnings("unchecked")
    public ResourceBinder(ExoContainerContext containerContext, MethodInvokerFactory invokerFactory) throws Exception
@@ -127,8 +131,8 @@
       // Initialize RuntimeDelegate instance
       // This is first component in life cycle what needs.
       // TODO better solution to initialize RuntimeDelegate
-      rd = new RuntimeDelegateImpl();
-      RuntimeDelegate.setInstance(rd);
+      RuntimeDelegate.setInstance(new RuntimeDelegateImpl());
+      rd = RuntimeDelegate.getInstance();
 
       ExoContainer container = containerContext.getContainer();
 
@@ -150,16 +154,17 @@
       // process them to be add as root resources.
       for (Object resource : container.getComponentInstancesOfType(ResourceContainer.class))
       {
-         bind(resource);
+         try
+         {
+            addResource(resource, null);
+         }
+         catch (Exception e)
+         {
+            LOG.error("Failed add JAX-RS resource " + resource.getClass().getName(), e);
+         }
       }
-
    }
 
-   public ResourceBinder(ExoContainerContext containerContext) throws Exception
-   {
-      this(containerContext, null);
-   }
-
    /**
     * @param application Application
     * @see Application
@@ -208,7 +213,7 @@
          }
          else
          {
-            bind(obj); // singleton resource
+            addResource(obj, null); // singleton resource
          }
       }
       for (Class clazz : application.getClasses())
@@ -251,221 +256,252 @@
          }
          else
          {
-            bind(clazz); // per-request resource
+            addResource(clazz, null); // per-request resource
          }
       }
    }
 
    /**
-    * Register supplied Object as root resource if it has valid JAX-RS
-    * annotations and no one resource with the same UriPattern already
+    * Register supplied class as per-request root resource if it has valid
+    * JAX-RS annotations and no one resource with the same UriPattern already
     * registered.
     *
-    * @param resource candidate to be root resource
-    * @return true if resource was bound and false if resource was not bound
-    *         cause it is not root resource
+    * @param resourceClass class of candidate to be root resource
+    * @param properties optional resource properties. It may contains additional
+    *        info about resource, e.g. description of resource, its
+    *        responsibility, etc. This info can be retrieved
+    *        {@link ObjectModel#getProperties()}. This parameter may be
+    *        <code>null</code>
+    * @throws ResourcePublicationException if resource can't be published
+    *         because to:
+    *         <ul>
+    *         <li>&#64javax.ws.rs.Path annotation is missing</li>
+    *         <li>resource has not any method with JAX-RS annotations</li>
+    *         <li>JAX-RS annotations are ambiguous or invalid</li>
+    *         <li>resource with the sane {@link UriPattern} already registered</li>
+    *         </ul>
+    * @see ObjectModel#getProperties()
+    * @see ObjectModel#getProperty(String)
     */
-   public boolean bind(final Object resource)
+   public void addResource(final Class<?> resourceClass, MultivaluedMap<String, String> properties)
    {
-      final Path path = resource.getClass().getAnnotation(Path.class);
-
-      AbstractResourceDescriptor descriptor = null;
-      if (path != null)
+      Path path = resourceClass.getAnnotation(Path.class);
+      if (path == null)
       {
-         try
-         {
-            descriptor = new AbstractResourceDescriptorImpl(resource, invokerFactory);
-         }
-         catch (Exception e)
-         {
-            String msg = "Unexpected error occurs when process resource class " + resource.getClass().getName();
-            LOG.error(msg, e);
-            return false;
-         }
+         throw new ResourcePublicationException("Resource class " + resourceClass.getName()
+            + " it is not root resource. " + "Path annotation javax.ws.rs.Path is not specified for this class.");
       }
-      else
+      try
       {
-         String msg =
-            "Resource class " + resource.getClass().getName() + " it is not root resource. "
-               + "Path annotation javax.ws.rs.Path is not specified for this class.";
-         LOG.warn(msg);
-         return false;
+         AbstractResourceDescriptor descriptor = new AbstractResourceDescriptorImpl(resourceClass, invokerFactory);
+         // validate AbstractResourceDescriptor
+         descriptor.accept(rdv);
+         if (properties != null)
+            descriptor.getProperties().putAll(properties);
+         addResource(new PerRequestObjectFactory<AbstractResourceDescriptor>(descriptor));
       }
+      catch (Exception e)
+      {
+         throw new ResourcePublicationException(e.getMessage());
+      }
+   }
 
-      // validate AbstractResourceDescriptor
+   /**
+    * Register supplied Object as singleton root resource if it has valid JAX-RS
+    * annotations and no one resource with the same UriPattern already
+    * registered.
+    *
+    * @param resource candidate to be root resource
+    * @param properties optional resource properties. It may contains additional
+    *        info about resource, e.g. description of resource, its
+    *        responsibility, etc. This info can be retrieved
+    *        {@link ObjectModel#getProperties()}. This parameter may be
+    *        <code>null</code>
+    * @throws ResourcePublicationException if resource can't be published
+    *         because to:
+    *         <ul>
+    *         <li>&#64javax.ws.rs.Path annotation is missing</li>
+    *         <li>resource has not any method with JAX-RS annotations</li>
+    *         <li>JAX-RS annotations are ambiguous or invalid</li>
+    *         <li>resource with the sane {@link UriPattern} already registered</li>
+    *         </ul>
+    * @see ObjectModel#getProperties()
+    * @see ObjectModel#getProperty(String)
+    */
+   public void addResource(final Object resource, MultivaluedMap<String, String> properties)
+   {
+      Path path = resource.getClass().getAnnotation(Path.class);
+      if (path == null)
+      {
+         throw new ResourcePublicationException("Resource class " + resource.getClass().getName()
+            + " it is not root resource. " + "Path annotation javax.ws.rs.Path is not specified for this class.");
+      }
       try
       {
+         AbstractResourceDescriptor descriptor = new AbstractResourceDescriptorImpl(resource, invokerFactory);
+         // validate AbstractResourceDescriptor
          descriptor.accept(rdv);
+         if (properties != null)
+            descriptor.getProperties().putAll(properties);
+         addResource(new SingletonObjectFactory<AbstractResourceDescriptor>(descriptor, resource));
       }
       catch (Exception e)
       {
-         LOG.error("Validation of root resource failed. ", e);
-         return false;
+         throw new ResourcePublicationException(e.getMessage());
       }
+   }
 
+   /**
+    * Register supplied root resource if no one resource with the same
+    * UriPattern already registered.
+    *
+    * @param resourceFactory root resource
+    * @throws ResourcePublicationException if resource can't be published
+    *         because resource with the sane {@link UriPattern} already
+    *         registered
+    */
+   public void addResource(final ObjectFactory<AbstractResourceDescriptor> resourceFactory)
+   {
+      UriPattern pattern = resourceFactory.getObjectModel().getUriPattern();
       synchronized (rootResources)
       {
-         // check does exist other resource with the same URI pattern
-         for (ObjectFactory<AbstractResourceDescriptor> exist : rootResources)
+         for (ObjectFactory<AbstractResourceDescriptor> resource : rootResources)
          {
-            if (exist.getObjectModel().getUriPattern().equals(descriptor.getUriPattern()))
+            if (resource.getObjectModel().getUriPattern().equals(resourceFactory.getObjectModel().getUriPattern()))
             {
-               String msg =
-                  "Resource class " + descriptor.getObjectClass().getName() + " can't be registered. Resource class "
-                     + exist.getClass().getName() + " with the same pattern "
-                     + exist.getObjectModel().getUriPattern().getTemplate() + " already registered.";
-               LOG.warn(msg);
-               return false;
+               throw new ResourcePublicationException("Resource class "
+                  + resourceFactory.getObjectModel().getObjectClass().getName()
+                  + " can't be registered. Resource class " + resource.getObjectModel().getObjectClass().getName()
+                  + " with the same pattern " + pattern + " already registered.");
             }
          }
-
-         // Singleton resource
-         ObjectFactory<AbstractResourceDescriptor> res =
-            new SingletonObjectFactory<AbstractResourceDescriptor>(descriptor, resource);
-         rootResources.add(res);
+         rootResources.add(resourceFactory);
          Collections.sort(rootResources, RESOURCE_COMPARATOR);
-         LOG.info("Bind new resource " + res.getObjectModel().getUriPattern().getTemplate() + " : "
-            + descriptor.getObjectClass());
+         size++;
+         if (LOG.isDebugEnabled())
+            LOG.debug("Add resource: " + resourceFactory.getObjectModel());
       }
-      size++;
-      return true;
    }
 
    /**
+    * Register root resource.
+    *
     * @param resourceClass class of candidate to be root resource
     * @return true if resource was bound and false if resource was not bound
     *         cause it is not root resource
+    * @deprecated use {@link #addResource(Class, MultivaluedMap)} instead
     */
    public boolean bind(final Class<?> resourceClass)
    {
-      final Path path = resourceClass.getAnnotation(Path.class);
-
-      AbstractResourceDescriptor descriptor = null;
-      if (path != null)
+      try
       {
-         try
-         {
-            descriptor = new AbstractResourceDescriptorImpl(resourceClass, invokerFactory);
-         }
-         catch (Exception e)
-         {
-            String msg = "Unexpected error occurs when process resource class " + resourceClass.getName();
-            LOG.error(msg, e);
-            return false;
-         }
+         addResource(resourceClass, null);
+         return true;
       }
-      else
+      catch (ResourcePublicationException e)
       {
-         String msg =
-            "Resource class " + resourceClass.getName() + " it is not root resource. "
-               + "Path annotation javax.ws.rs.Path is not specified for this class.";
-         LOG.warn(msg);
+         LOG.warn(e.getMessage());
          return false;
       }
+   }
 
-      // validate AbstractResourceDescriptor
+   /**
+    * Register supplied Object as singleton root resource if it has valid JAX-RS
+    * annotations and no one resource with the same UriPattern already
+    * registered.
+    *
+    * @param resource candidate to be root resource
+    * @return true if resource was bound and false if resource was not bound
+    *         cause it is not root resource
+    * @deprecated use {@link #addResource(Object, MultivaluedMap)} instead
+    */
+   public boolean bind(final Object resource)
+   {
       try
       {
-         descriptor.accept(rdv);
+         addResource(resource, null);
+         return true;
       }
-      catch (Exception e)
+      catch (ResourcePublicationException e)
       {
-         LOG.error("Validation of root resource failed. ", e);
+         LOG.warn(e.getMessage());
          return false;
       }
-
-      synchronized (rootResources)
-      {
-         // check does exist other resource with the same URI pattern
-         for (ObjectFactory<AbstractResourceDescriptor> exist : rootResources)
-         {
-            AbstractResourceDescriptor existDescriptor = exist.getObjectModel();
-            if (exist.getObjectModel().getUriPattern().equals(descriptor.getUriPattern()))
-            {
-
-               String msg =
-                  "Resource class " + descriptor.getObjectClass().getName() + " can't be registered. Resource class "
-                     + existDescriptor.getObjectClass().getName() + " with the same pattern "
-                     + exist.getObjectModel().getUriPattern().getTemplate() + " already registered.";
-               LOG.warn(msg);
-               return false;
-            }
-         }
-         // per-request resource
-         ObjectFactory<AbstractResourceDescriptor> res =
-            new PerRequestObjectFactory<AbstractResourceDescriptor>(descriptor);
-         rootResources.add(res);
-         Collections.sort(rootResources, RESOURCE_COMPARATOR);
-         LOG.info("Bind new resource " + res.getObjectModel().getUriPattern().getRegex() + " : " + resourceClass);
-      }
-      size++;
-      return true;
    }
 
    /**
-    * Remove root resource of supplied class from root resource collection.
-    *
-    * @param clazz root resource class
-    * @return true if resource was unbound false otherwise
+    * Clear the list of ResourceContainer description.
     */
-   @SuppressWarnings("unchecked")
-   public boolean unbind(Class clazz)
+   public void clear()
    {
       synchronized (rootResources)
       {
-         Iterator<ObjectFactory<AbstractResourceDescriptor>> i = rootResources.iterator();
-         while (i.hasNext())
-         {
-            ObjectFactory<AbstractResourceDescriptor> res = i.next();
-            Class c = res.getObjectModel().getObjectClass();
-            if (clazz.equals(c))
-            {
-               i.remove();
-               LOG.info("Remove ResourceContainer " + res.getObjectModel().getUriPattern().getTemplate() + " : " + c);
-               size--;
-               return true;
-            }
-         }
-         return false;
+         rootResources.clear();
+         size = 0;
       }
    }
 
-   public boolean unbind(String uriTemplate)
+   /**
+    * Get root resource matched to <code>requestPath</code>.
+    *
+    * @param requestPath request path
+    * @param parameterValues see {@link ApplicationContext#getParameterValues()}
+    * @return root resource matched to <code>requestPath</code> or
+    *         <code>null</code>
+    */
+   public ObjectFactory<AbstractResourceDescriptor> getMatchedResource(String requestPath, List<String> parameterValues)
    {
+      ObjectFactory<AbstractResourceDescriptor> resourceFactory = null;
       synchronized (rootResources)
       {
-         Iterator<ObjectFactory<AbstractResourceDescriptor>> i = rootResources.iterator();
-         while (i.hasNext())
+         for (ObjectFactory<AbstractResourceDescriptor> resource : rootResources)
          {
-            ObjectFactory<AbstractResourceDescriptor> res = i.next();
-            String t = res.getObjectModel().getUriPattern().getTemplate();
-            if (t.equals(uriTemplate))
+            if (resource.getObjectModel().getUriPattern().match(requestPath, parameterValues))
             {
-               i.remove();
-               LOG.info("Remove ResourceContainer " + res.getObjectModel().getUriPattern().getTemplate());
-               size--;
-               return true;
+               // all times will at least 1
+               int len = parameterValues.size();
+               // If capturing group contains last element and this element is
+               // neither null nor '/' then ResourceClass must contains at least one
+               // sub-resource method or sub-resource locator.
+               if (parameterValues.get(len - 1) != null && !parameterValues.get(len - 1).equals("/"))
+               {
+                  int subresnum =
+                     resource.getObjectModel().getSubResourceMethods().size()
+                        + resource.getObjectModel().getSubResourceLocators().size();
+                  if (subresnum == 0)
+                     continue;
+               }
+               resourceFactory = resource;
+               break;
             }
          }
-         return false;
       }
+      return resourceFactory;
    }
 
    /**
-    * Clear the list of ResourceContainer description.
+    * @return all registered root resources
     */
-   public void clear()
+   public List<ObjectFactory<AbstractResourceDescriptor>> getResources()
    {
-      rootResources.clear();
-      size = 0;
+      return rootResources;
    }
 
    /**
     * @return all registered root resources
     */
-   public List<ObjectFactory<AbstractResourceDescriptor>> getResources()
+   @Deprecated
+   public List<AbstractResourceDescriptor> getRootResources()
    {
-      return rootResources;
+      List<AbstractResourceDescriptor> l = new ArrayList<AbstractResourceDescriptor>(size);
+      synchronized (rootResources)
+      {
+         for (ObjectFactory<AbstractResourceDescriptor> f : rootResources)
+         {
+            l.add(f.getObjectModel());
+         }
+      }
+      return l;
    }
 
    /**
@@ -477,20 +513,98 @@
    }
 
    /**
-    * @return all registered root resources
+    * Remove root resource of supplied class from root resource collection.
+    *
+    * @param clazz root resource class
+    * @return removed resource or <code>null</code> if resource of specified
+    *         class not found
     */
-   @Deprecated
-   public List<AbstractResourceDescriptor> getRootResources()
+   @SuppressWarnings("unchecked")
+   public ObjectFactory<AbstractResourceDescriptor> removeResource(Class clazz)
    {
-      List<AbstractResourceDescriptor> l = new ArrayList<AbstractResourceDescriptor>(rootResources.size());
+      ObjectFactory<AbstractResourceDescriptor> resource = null;
       synchronized (rootResources)
       {
-         for (ObjectFactory<AbstractResourceDescriptor> f : rootResources)
+         for (Iterator<ObjectFactory<AbstractResourceDescriptor>> iter = rootResources.iterator(); iter.hasNext()
+            && resource == null;)
          {
-            l.add(f.getObjectModel());
+            ObjectFactory<AbstractResourceDescriptor> next = iter.next();
+            Class<?> resourceClass = next.getObjectModel().getObjectClass();
+            if (clazz.equals(resourceClass))
+            {
+               iter.remove();
+               resource = next;
+            }
          }
+         if (resource != null)
+         {
+            size--;
+            if (LOG.isDebugEnabled())
+               LOG.debug("Remove resource: " + resource.getObjectModel());
+         }
       }
-      return l;
+      return resource;
    }
 
+   /**
+    * Remove root resource with specified UriTemplate from root resource
+    * collection.
+    *
+    * @param path root resource path
+    * @return removed resource or <code>null</code> if resource for specified
+    *         template not found
+    */
+   public ObjectFactory<AbstractResourceDescriptor> removeResource(String path)
+   {
+      ObjectFactory<AbstractResourceDescriptor> resource = null;
+      UriPattern pattern = new UriPattern(path);
+      synchronized (rootResources)
+      {
+         for (Iterator<ObjectFactory<AbstractResourceDescriptor>> iter = rootResources.iterator(); iter.hasNext()
+            && resource == null;)
+         {
+            ObjectFactory<AbstractResourceDescriptor> next = iter.next();
+            UriPattern resourcePattern = next.getObjectModel().getUriPattern();
+            if (pattern.equals(resourcePattern))
+            {
+               iter.remove();
+               resource = next;
+            }
+         }
+         if (resource != null)
+         {
+            size--;
+            if (LOG.isDebugEnabled())
+               LOG.debug("Remove resource: " + resource.getObjectModel());
+         }
+      }
+      return resource;
+   }
+
+   /**
+    * Remove root resource of supplied class from root resource collection.
+    *
+    * @param clazz root resource class
+    * @return true if resource was unbound false otherwise
+    * @deprecated use {@link #removeResource(Class)}
+    */
+   @SuppressWarnings("unchecked")
+   public boolean unbind(Class clazz)
+   {
+      return null != removeResource(clazz);
+   }
+
+   /**
+    * Remove root resource with specified UriTemplate from root resource
+    * collection.
+    *
+    * @param path root resource path
+    * @return true if resource was unbound false otherwise
+    * @deprecated use {@link #removeResource(String)} instead
+    */
+   public boolean unbind(String path)
+   {
+      return null != removeResource(path);
+   }
+
 }

Added: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ResourcePublicationException.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ResourcePublicationException.java	                        (rev 0)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ResourcePublicationException.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -0,0 +1,39 @@
+/**
+ * Copyright (C) 2010 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.exoplatform.services.rest.impl;
+
+/**
+ * Throws if root resource can't be published, e.g. resource can't be registered
+ * because to conflict of root path
+ *
+ * @author <a href="mailto:andrew00x at gmail.com">Andrey Parfonov</a>
+ * @version $Id$
+ */
+public class ResourcePublicationException extends RuntimeException
+{
+
+   private static final long serialVersionUID = -7293299406099524107L;
+
+   public ResourcePublicationException(String message)
+   {
+      super(message);
+   }
+
+}


Property changes on: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/ResourcePublicationException.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/method/DefaultMethodInvoker.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/method/DefaultMethodInvoker.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/method/DefaultMethodInvoker.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -28,15 +28,16 @@
 import org.exoplatform.services.rest.method.MethodInvokerFilter;
 import org.exoplatform.services.rest.resource.GenericMethodResource;
 
-import java.io.IOException;
 import java.io.InputStream;
 import java.lang.annotation.Annotation;
 import java.lang.reflect.InvocationTargetException;
+import java.util.List;
 
 import javax.ws.rs.MatrixParam;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.QueryParam;
 import javax.ws.rs.WebApplicationException;
+import javax.ws.rs.core.HttpHeaders;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
@@ -84,13 +85,16 @@
             catch (Exception e)
             {
 
+               String msg = "Not able resolve method parameter " + mp;
                Class<?> ac = a.annotationType();
                if (ac == MatrixParam.class || ac == QueryParam.class || ac == PathParam.class)
                {
-                  throw new WebApplicationException(e, Response.status(Response.Status.NOT_FOUND).build());
+                  throw new WebApplicationException(e, Response.status(Response.Status.NOT_FOUND).entity(msg).type(
+                     MediaType.TEXT_PLAIN).build());
                }
 
-               throw new WebApplicationException(e, Response.status(Response.Status.BAD_REQUEST).build());
+               throw new WebApplicationException(e, Response.status(Response.Status.BAD_REQUEST).entity(msg).type(
+                  MediaType.TEXT_PLAIN).build());
 
             }
 
@@ -106,37 +110,62 @@
             else
             {
                MediaType contentType = context.getContainerRequest().getMediaType();
-               MultivaluedMap<String, String> headers = context.getContainerRequest().getRequestHeaders();
 
                MessageBodyReader entityReader =
                   context.getProviders().getMessageBodyReader(mp.getParameterClass(), mp.getGenericType(),
                      mp.getAnnotations(), contentType);
                if (entityReader == null)
                {
-                  if (LOG.isDebugEnabled())
+                  List<String> contentLength =
+                     context.getContainerRequest().getRequestHeader(HttpHeaders.CONTENT_LENGTH);
+                  int length = 0;
+                  if (contentLength != null && contentLength.size() > 0)
                   {
-                     LOG.warn("Unsupported media type. ");
+                     length = Integer.parseInt(contentLength.get(0));
                   }
 
-                  throw new WebApplicationException(Response.status(Response.Status.UNSUPPORTED_MEDIA_TYPE).build());
+                  if (contentType == null && length == 0)
+                  {
+                     // If both Content-Length and Content-Type is not set
+                     // consider there is no content. In this case we not able
+                     // to determine reader required for content but
+                     // 'Unsupported Media Type' (415) status looks strange if
+                     // there is no content at all.
+                     p[i++] = null;
+                  }
+                  else
+                  {
+                     String msg =
+                        "Media type " + contentType + " is not supported. There is no corresponded entity reader.";
+                     if (LOG.isDebugEnabled())
+                     {
+                        LOG.warn(msg);
+                     }
+                     throw new WebApplicationException(Response.status(Response.Status.UNSUPPORTED_MEDIA_TYPE).entity(
+                        msg).type(MediaType.TEXT_PLAIN).build());
+                  }
                }
-
-               try
+               else
                {
-                  p[i++] =
-                     entityReader.readFrom(mp.getParameterClass(), mp.getGenericType(), mp.getAnnotations(),
-                        contentType, headers, entityStream);
-               }
-               catch (IOException e)
-               {
-
-                  if (LOG.isDebugEnabled())
+                  try
                   {
-                     e.printStackTrace();
+                     MultivaluedMap<String, String> headers = context.getContainerRequest().getRequestHeaders();
+                     p[i++] =
+                        entityReader.readFrom(mp.getParameterClass(), mp.getGenericType(), mp.getAnnotations(),
+                           contentType, headers, entityStream);
                   }
-
-                  throw new InternalException(e);
-
+                  catch (Exception e)
+                  {
+                     if (LOG.isDebugEnabled())
+                     {
+                        e.printStackTrace();
+                     }
+                     if (e instanceof WebApplicationException)
+                     {
+                        throw (WebApplicationException)e;
+                     }
+                     throw new InternalException(e);
+                  }
                }
             }
          }

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/provider/ProviderDescriptorImpl.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/provider/ProviderDescriptorImpl.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/provider/ProviderDescriptorImpl.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -23,6 +23,7 @@
 import org.exoplatform.services.rest.FieldInjector;
 import org.exoplatform.services.rest.impl.ConstructorDescriptorImpl;
 import org.exoplatform.services.rest.impl.FieldInjectorImpl;
+import org.exoplatform.services.rest.impl.MultivaluedMapImpl;
 import org.exoplatform.services.rest.impl.header.MediaTypeHelper;
 import org.exoplatform.services.rest.provider.ProviderDescriptor;
 import org.exoplatform.services.rest.resource.ResourceDescriptorVisitor;
@@ -35,6 +36,7 @@
 import javax.ws.rs.Consumes;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
 
 /**
  * @author <a href="mailto:andrew00x at gmail.com">Andrey Parfonov</a>
@@ -50,7 +52,7 @@
 
    /**
     * Resource class constructors.
-    * 
+    *
     * @see {@link ConstructorDescriptor}
     */
    private final List<ConstructorDescriptor> constructors;
@@ -72,6 +74,9 @@
     */
    private final List<MediaType> produces;
 
+   /** Optional data. */
+   private MultivaluedMap<String, String> properties;
+
    /**
     * @param providerClass provider class
     */
@@ -168,6 +173,30 @@
    /**
     * {@inheritDoc}
     */
+   public MultivaluedMap<String, String> getProperties()
+   {
+      if (properties == null)
+      {
+         properties = new MultivaluedMapImpl();
+      }
+      return properties;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public List<String> getProperty(String key)
+   {
+      if (properties != null)
+      {
+         return properties.get(key);
+      }
+      return null;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
    public List<MediaType> produces()
    {
       return produces;

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/resource/AbstractResourceDescriptorImpl.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/resource/AbstractResourceDescriptorImpl.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/resource/AbstractResourceDescriptorImpl.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -131,9 +131,7 @@
     */
    private final List<FieldInjector> fields;
 
-   /**
-    * Optional data
-    */
+   /** Optional data. */
    private MultivaluedMap<String, String> properties;
 
    private final MethodInvokerFactory invokerFactory;

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/resource/PathValue.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/resource/PathValue.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/impl/resource/PathValue.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -20,7 +20,7 @@
 
 /**
  * Describe the Path annotation, see {@link javax.ws.rs.Path}.
- * 
+ *
  * @author <a href="mailto:andrew00x at gmail.com">Andrey Parfonov</a>
  * @version $Id: $
  */
@@ -53,7 +53,7 @@
     */
    public String toString()
    {
-      return "( " + path + " )";
+      return "(" + path + ")";
    }
 
 }

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/resource/AbstractResourceDescriptor.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/resource/AbstractResourceDescriptor.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/resource/AbstractResourceDescriptor.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -22,14 +22,10 @@
 import org.exoplatform.services.rest.impl.resource.PathValue;
 import org.exoplatform.services.rest.uri.UriPattern;
 
-import java.util.List;
-
-import javax.ws.rs.core.MultivaluedMap;
-
 /**
  * Describe Resource Class or Root Resource Class. Resource Class is any Java
  * class that uses JAX-RS annotations to implement corresponding Web resource.
- * 
+ *
  * @author <a href="mailto:andrew00x at gmail.com">Andrey Parfonov</a>
  * @version $Id: $
  */
@@ -58,21 +54,6 @@
     * @return sub-resource methods
     */
    SubResourceMethodMap getSubResourceMethods();
-   
-   /**
-    * Optional data
-    * 
-    * @param key
-    * @return property by key 
-    */
-   List<String> getProperty(String key);
-   
-   /**
-    * Optional data
-    * 
-    * @return all properties
-    */
-   MultivaluedMap<String,String> getProperties();
 
    /**
     * @return See {@link UriPattern}

Added: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/tools/DummySecurityContext.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/tools/DummySecurityContext.java	                        (rev 0)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/tools/DummySecurityContext.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -0,0 +1,95 @@
+/**
+ * Copyright (C) 2010 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+
+package org.exoplatform.services.rest.tools;
+
+import org.exoplatform.services.rest.impl.ContainerRequest;
+
+import java.io.InputStream;
+import java.net.URI;
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.ws.rs.core.MultivaluedMap;
+
+/**
+ * @author <a href="mailto:andrew00x at gmail.com">Andrey Parfonov</a>
+ * @version $Id$
+ */
+class DummySecurityContext extends ContainerRequest
+{
+   private DummyPrincipal dummyPrincipal;
+
+   public DummySecurityContext(String method, URI requestUri, URI baseUri, InputStream entityStream,
+      MultivaluedMap<String, String> httpHeaders)
+   {
+      super(method, requestUri, baseUri, entityStream, httpHeaders);
+      dummyPrincipal = new DummyPrincipal();
+   }
+
+   @Override
+   public String getAuthenticationScheme()
+   {
+      // Consider as Basic Authentication
+      return BASIC_AUTH;
+   }
+
+   @Override
+   public Principal getUserPrincipal()
+   {
+      return dummyPrincipal;
+   }
+
+   @Override
+   public boolean isSecure()
+   {
+      return false;
+   }
+
+   @Override
+   public boolean isUserInRole(String role)
+   {
+      return dummyPrincipal.isUserInRole(role);
+   }
+
+   class DummyPrincipal implements Principal
+   {
+
+      Set<String> roles = new HashSet<String>();
+
+      public DummyPrincipal()
+      {
+         roles.add("administrators");
+         roles.add("users");
+      }
+
+      public String getName()
+      {
+         return "root";
+      }
+
+      public boolean isUserInRole(String role)
+      {
+         return roles.contains(role);
+      }
+
+   }
+
+}


Property changes on: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/tools/DummySecurityContext.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/tools/ResourceLauncher.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/tools/ResourceLauncher.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/tools/ResourceLauncher.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -77,8 +77,10 @@
       if (writer == null)
          writer = new DummyContainerResponseWriter();
 
+      //      ContainerRequest request =
+      //         new ContainerRequest(method, new URI(requestURI), new URI(baseURI), in, new InputHeadersMap(headers));
       ContainerRequest request =
-         new ContainerRequest(method, new URI(requestURI), new URI(baseURI), in, new InputHeadersMap(headers));
+         new DummySecurityContext(method, new URI(requestURI), new URI(baseURI), in, new InputHeadersMap(headers));
       ContainerResponse response = new ContainerResponse(writer);
       requestHandler.handleRequest(request, response);
       return response;

Modified: ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/uri/UriPattern.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/uri/UriPattern.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/main/java/org/exoplatform/services/rest/uri/UriPattern.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -39,10 +39,10 @@
     * Sort the templates according to the string comparison of the template
     * regular expressions.
     * <p>
-    * JSR-311 specification: "Sort the set of matching resource classes using the
-    * number of characters in the regular expression not resulting from template
-    * variables as the primary key and the number of matching groups as a
-    * secondary key"
+    * JSR-311 specification: "Sort the set of matching resource classes using
+    * the number of characters in the regular expression not resulting from
+    * template variables as the primary key and the number of matching groups as
+    * a secondary key"
     * </p>
     */
    public static final Comparator<UriPattern> URIPATTERN_COMPARATOR = new UriPatternComparator();
@@ -76,7 +76,7 @@
          if (o1.getNumberOfLiteralCharacters() > o2.getNumberOfLiteralCharacters())
             return -1;
 
-         // pattern with two variables less the pattern with four variables 
+         // pattern with two variables less the pattern with four variables
          if (o1.getParameterNames().size() < o2.getParameterNames().size())
             return 1;
          if (o1.getParameterNames().size() > o2.getParameterNames().size())
@@ -129,7 +129,7 @@
 
    /**
     * Constructs UriPattern.
-    * 
+    *
     * @param template the source template
     * @see {@link javax.ws.rs.Path}
     */
@@ -180,12 +180,12 @@
     */
    public int hashCode()
    {
-      return template.hashCode() + regex.hashCode();
+      return getRegex().hashCode();
    }
 
    /**
     * Get the regex pattern.
-    * 
+    *
     * @return the regex pattern
     */
    public Pattern getPattern()
@@ -195,7 +195,7 @@
 
    /**
     * Get the URI template as a String.
-    * 
+    *
     * @return the URI template
     */
    public String getTemplate()
@@ -205,7 +205,7 @@
 
    /**
     * Get the regular expression.
-    * 
+    *
     * @return the regular expression
     */
    public String getRegex()
@@ -215,7 +215,7 @@
 
    /**
     * Get the number of literal characters in the template.
-    * 
+    *
     * @return number of literal characters in the template
     */
    public int getNumberOfLiteralCharacters()
@@ -237,7 +237,7 @@
     * greater then number of keys. It can be used for check is resource is
     * matching to requested. If resource is match the last element in list must
     * be '/' or null.
-    * 
+    *
     * @param uri the URI string
     * @param parameters target list
     * @return true if URI string is match to pattern, false otherwise
@@ -289,7 +289,7 @@
 
    /**
     * Create URI from URI part. Each URI part can contains templates.
-    * 
+    *
     * @param schema the schema URI part
     * @param userInfo the user info URI part
     * @param host the host name URI part
@@ -299,7 +299,7 @@
     * @param fragment the fragment URI part
     * @param values the values which must be used instead templates parameters
     * @param encode if true then encode value before add it in URI, otherwise
-    *          value must be validate to legal characters
+    *        value must be validate to legal characters
     * @return the URI string
     */
    public static String createUriWithValues(String schema, String userInfo, String host, int port, String path,
@@ -334,7 +334,7 @@
 
       if (path != null)
       {
-         if (sb.length() > 0 && path.charAt(0) != '/')
+         if (sb.length() > 0 && path.length() > 0 && path.charAt(0) != '/')
             sb.append('/');
          appendUriPart(sb, path, UriComponent.PATH, values, encode);
       }
@@ -356,7 +356,7 @@
 
    /**
     * Create URI from URI part. Each URI part can contains templates.
-    * 
+    *
     * @param schema the schema URI part
     * @param userInfo the user info URI part
     * @param host the host name URI part
@@ -366,7 +366,7 @@
     * @param fragment the fragment URI part
     * @param values the values which must be used instead templates parameters
     * @param encode if true then encode value before add it in URI, otherwise
-    *          value must be validate to legal characters
+    *        value must be validate to legal characters
     * @return the URI string
     */
    public static String createUriWithValues(String schema, String userInfo, String host, int port, String path,
@@ -404,7 +404,7 @@
 
       if (path != null)
       {
-         if (sb.length() > 0 && path.charAt(0) != '/')
+         if (sb.length() > 0 && path.length() > 0 && path.charAt(0) != '/')
             sb.append('/');
          p = appendUriPart(sb, path, UriComponent.PATH, values, p, m, encode);
       }
@@ -431,7 +431,7 @@
     * @param component the URI component
     * @param values values map
     * @param encode if true then encode value before add it in URI, otherwise
-    *          value must be validate to legal characters
+    *        value must be validate to legal characters
     */
    private static void appendUriPart(StringBuffer sb, String uriPart, int component,
       Map<String, ? extends Object> values, boolean encode)
@@ -471,17 +471,17 @@
     * @param component the URI component
     * @param sourceValues the source array of values
     * @param offset the offset in array
-    * @param values values map, keep parameter/value pair which have been already
-    *          found. From java docs:
-    *          <p>
-    *          All instances of the same template parameter will be replaced by
-    *          the same value that corresponds to the position of the first
-    *          instance of the template parameter. e.g. the template
-    *          "{a}/{b}/{a}" with values {"x", "y", "z"} will result in the the
-    *          URI "x/y/x", <i>not</i> "x/y/z".
-    *          </p>
+    * @param values values map, keep parameter/value pair which have been
+    *        already found. From java docs:
+    *        <p>
+    *        All instances of the same template parameter will be replaced by
+    *        the same value that corresponds to the position of the first
+    *        instance of the template parameter. e.g. the template "{a}/{b}/{a}"
+    *        with values {"x", "y", "z"} will result in the the URI "x/y/x",
+    *        <i>not</i> "x/y/z".
+    *        </p>
     * @param encode if true then encode value before add it in URI, otherwise
-    *          value must be validate to legal characters
+    *        value must be validate to legal characters
     * @return offset
     */
    private static int appendUriPart(StringBuffer sb, String uriPart, int component, Object[] sourceValues, int offset,
@@ -539,7 +539,7 @@
 
    /**
     * Check does given URI string has templates.
-    * 
+    *
     * @param uri the URI which must be checked
     * @return true if URI has templates false otherwise
     */

Modified: ws/trunk/exo.ws.rest.core/src/test/java/org/exoplatform/services/rest/BaseTest.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/test/java/org/exoplatform/services/rest/BaseTest.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/test/java/org/exoplatform/services/rest/BaseTest.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -63,28 +63,28 @@
    {
    }
 
-   public boolean registry(Object resource) throws Exception
+   public void registry(Object resource) throws Exception
    {
       //    container.registerComponentInstance(resource);
-      return binder.bind(resource);
+      binder.addResource(resource, null);
    }
 
-   public boolean registry(Class<?> resourceClass) throws Exception
+   public void registry(Class<?> resourceClass) throws Exception
    {
       //    container.registerComponentImplementation(resourceClass.getName(), resourceClass);
-      return binder.bind(resourceClass);
+      binder.addResource(resourceClass, null);
    }
 
-   public boolean unregistry(Object resource)
+   public void unregistry(Object resource)
    {
       //    container.unregisterComponentByInstance(resource);
-      return binder.unbind(resource.getClass());
+      binder.removeResource(resource.getClass());
    }
 
-   public boolean unregistry(Class<?> resourceClass)
+   public void unregistry(Class<?> resourceClass)
    {
       //    container.unregisterComponent(resourceClass.getName());
-      return binder.unbind(resourceClass);
+      binder.removeResource(resourceClass);
    }
 
 }

Modified: ws/trunk/exo.ws.rest.core/src/test/java/org/exoplatform/services/rest/impl/ResourceBinderTest.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/test/java/org/exoplatform/services/rest/impl/ResourceBinderTest.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/test/java/org/exoplatform/services/rest/impl/ResourceBinderTest.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -45,14 +45,14 @@
 
    public void testBind()
    {
-      binder.bind(Resource.class);
+      binder.addResource(Resource.class, null);
       assertEquals(1, binder.getSize());
    }
 
    public void testUnbind()
    {
-      binder.bind(Resource.class);
-      binder.unbind(Resource.class);
+      binder.addResource(Resource.class, null);
+      binder.removeResource(Resource.class);
       assertEquals(0, binder.getSize());
    }
 
@@ -95,24 +95,51 @@
 
    public void testSameResourceURI()
    {
-      assertTrue(binder.bind(SameURIResource1.class));
+      binder.addResource(SameURIResource1.class, null);
       assertEquals(1, binder.getSize());
-      assertFalse(binder.bind(SameURIResource2.class));
+      try
+      {
+         binder.addResource(SameURIResource2.class, null);
+      }
+      catch (ResourcePublicationException e)
+      {
+      }
       assertEquals(1, binder.getSize());
+
       binder.clear();
-      assertTrue(binder.bind(SameURIResource2.class));
+      binder.addResource(SameURIResource2.class, null);
       assertEquals(1, binder.getSize());
-      assertFalse(binder.bind(SameURIResource1.class));
+      try
+      {
+         binder.addResource(SameURIResource1.class, null);
+      }
+      catch (ResourcePublicationException e)
+      {
+      }
       assertEquals(1, binder.getSize());
+
       binder.clear();
-      assertTrue(binder.bind(new SameURIResource1()));
+      binder.addResource(new SameURIResource1(), null);
       assertEquals(1, binder.getSize());
-      assertFalse(binder.bind(new SameURIResource2()));
+      try
+      {
+         binder.addResource(new SameURIResource2(), null);
+      }
+      catch (ResourcePublicationException e)
+      {
+      }
       assertEquals(1, binder.getSize());
+
       binder.clear();
-      assertTrue(binder.bind(new SameURIResource2()));
+      binder.addResource(new SameURIResource2(), null);
       assertEquals(1, binder.getSize());
-      assertFalse(binder.bind(new SameURIResource1()));
+      try
+      {
+         binder.addResource(new SameURIResource1(), null);
+      }
+      catch (ResourcePublicationException e)
+      {
+      }
       assertEquals(1, binder.getSize());
    }
 

Modified: ws/trunk/exo.ws.rest.core/src/test/java/org/exoplatform/services/rest/uri/UriPatternTest.java
===================================================================
--- ws/trunk/exo.ws.rest.core/src/test/java/org/exoplatform/services/rest/uri/UriPatternTest.java	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/exo.ws.rest.core/src/test/java/org/exoplatform/services/rest/uri/UriPatternTest.java	2010-07-09 13:37:30 UTC (rev 2761)
@@ -129,15 +129,28 @@
 
    }
 
-   private static void testMatch(String pattern, String uri, String[] values)
+   private boolean log = false;
+
+   private void testMatch(String pattern, String uri, String[] values)
    {
       UriPattern p = new UriPattern(pattern);
-      System.out.println("URI:        " + uri);
-      System.out.println("REGEX:      " + p.getRegex());
+      if (log)
+      {
+         System.out.println("URI:        " + uri);
+         System.out.println("REGEX:      " + p.getRegex());
+         System.out.println("PATTERN:    " + pattern);
+         System.out.println("TEMPLATE:   " + p.getTemplate());
+      }
+
       List<String> l = new ArrayList<String>();
       assertTrue(p.match(uri, l));
-      System.out.println("PARAMETERS: " + p.getParameterNames());
-      System.out.println("VARIABLES:  " + l);
+
+      if (log)
+      {
+         System.out.println("PARAMETERS: " + p.getParameterNames());
+         System.out.println("VARIABLES:  " + l);
+         System.out.println();
+      }
       assertEquals(values.length, l.size());
       int i = 0;
       for (String t : l)

Modified: ws/trunk/pom.xml
===================================================================
--- ws/trunk/pom.xml	2010-07-09 10:16:44 UTC (rev 2760)
+++ ws/trunk/pom.xml	2010-07-09 13:37:30 UTC (rev 2761)
@@ -1,11 +1,23 @@
+   <!--
 
-	<!--
+    Copyright (C) 2009 eXo Platform SAS.
 
-		Copyright (C) 2009 eXo Platform SAS. This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at
-		your option) any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
-		License for more details. You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF
-		site: http://www.fsf.org.
-	-->
+    This is free software; you can redistribute it and/or modify it
+    under the terms of the GNU Lesser General Public License as
+    published by the Free Software Foundation; either version 2.1 of
+    the License, or (at your option) any later version.
+
+    This software is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this software; if not, write to the Free
+    Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+    02110-1301 USA, or see the FSF site: http://www.fsf.org.
+
+   -->
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 
 	<modelVersion>4.0.0</modelVersion>



More information about the exo-jcr-commits mailing list