Package org.exoplatform.services.rest.impl

Source Code of org.exoplatform.services.rest.impl.ResourceBinder$ResourceComparator

/*
* 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.
*/
package org.exoplatform.services.rest.impl;

import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.exoplatform.services.rest.Filter;
import org.exoplatform.services.rest.ObjectFactory;
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.resource.AbstractResourceDescriptorImpl;
import org.exoplatform.services.rest.impl.resource.ResourceDescriptorValidator;
import org.exoplatform.services.rest.method.MethodInvokerFilter;
import org.exoplatform.services.rest.resource.AbstractResourceDescriptor;
import org.exoplatform.services.rest.resource.ResourceContainer;
import org.exoplatform.services.rest.resource.ResourceDescriptorVisitor;
import org.exoplatform.services.rest.uri.UriPattern;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import javax.ws.rs.Path;
import javax.ws.rs.core.Application;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.ext.Provider;
import javax.ws.rs.ext.RuntimeDelegate;

/**
* Lookup for root resource eXo container components at startup and
* register/unregister resources via specified methods.
*
* @see AbstractResourceDescriptor
* @see SingletonResourceFactory
* @author <a href="mailto:andrew00x@gmail.com">Andrey Parfonov</a>
* @version $Id: $
*/
public class ResourceBinder
{

   /**
    * Logger.
    */
   private static final Log LOG = ExoLogger.getLogger("exo.ws.rest.core.ResourceBinder");

   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)
      {
         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.
    */
   protected final ResourceDescriptorVisitor rdv = ResourceDescriptorValidator.getInstance();

   protected int size = 0;

   /**
    * @see RuntimeDelegate
    */
   protected final RuntimeDelegate rd;

   /**
    * @param containerContext eXo container context
    * @throws Exception if can't set instance of {@link RuntimeDelegate}
    */
   @SuppressWarnings("unchecked")
   public ResourceBinder(ExoContainerContext containerContext) throws Exception
   {
      // Initialize RuntimeDelegate instance
      // This is first component in life cycle what needs.
      // TODO better solution to initialize RuntimeDelegate
      rd = new RuntimeDelegateImpl();
      RuntimeDelegate.setInstance(rd);

      ExoContainer container = containerContext.getContainer();

      // Lookup Applications
      List<Application> al = container.getComponentInstancesOfType(Application.class);
      for (Application a : al)
      {
         try
         {
            addApplication(a);
         }
         catch (Exception e)
         {
            LOG.error("Failed add JAX-RS application " + a.getClass().getName(), e);
         }
      }

      // Lookup all object which implements ResourceContainer interface and
      // process them to be add as root resources.
      for (Object resource : container.getComponentInstancesOfType(ResourceContainer.class))
      {
         bind(resource);
      }

   }

   /**
    * @param application Application
    * @see Application
    */
   @SuppressWarnings("unchecked")
   public void addApplication(Application application)
   {
      ProviderBinder providers = ProviderBinder.getInstance();
      for (Object obj : application.getSingletons())
      {
         if (obj.getClass().getAnnotation(Provider.class) != null)
         {
            // singleton provider
            if (obj instanceof ContextResolver)
            {
               providers.addContextResolver((ContextResolver)obj);
            }
            if (obj instanceof ExceptionMapper)
            {
               providers.addExceptionMapper((ExceptionMapper)obj);
            }
            if (obj instanceof MessageBodyReader)
            {
               providers.addMessageBodyReader((MessageBodyReader)obj);
            }
            if (obj instanceof MessageBodyWriter)
            {
               providers.addMessageBodyWriter((MessageBodyWriter)obj);
            }
         }
         else if (obj.getClass().getAnnotation(Filter.class) != null)
         {
            // singleton filter
            if (obj instanceof MethodInvokerFilter)
            {
               providers.addMethodInvokerFilter((MethodInvokerFilter)obj);
            }
            if (obj instanceof RequestFilter)
            {
               providers.addRequestFilter((RequestFilter)obj);
            }
            if (obj instanceof ResponseFilter)
            {
               providers.addResponseFilter((ResponseFilter)obj);
            }
         }
         else
         {
            bind(obj); // singleton resource
         }
      }
      for (Class clazz : application.getClasses())
      {
         if (clazz.getAnnotation(Provider.class) != null)
         {
            // per-request provider
            if (ContextResolver.class.isAssignableFrom(clazz))
            {
               providers.addContextResolver(clazz);
            }
            if (ExceptionMapper.class.isAssignableFrom(clazz))
            {
               providers.addExceptionMapper(clazz);
            }
            if (MessageBodyReader.class.isAssignableFrom(clazz))
            {
               providers.addMessageBodyReader(clazz);
            }
            if (MessageBodyWriter.class.isAssignableFrom(clazz))
            {
               providers.addMessageBodyWriter(clazz);
            }
         }
         else if (clazz.getAnnotation(Filter.class) != null)
         {
            // per-request filter
            if (MethodInvokerFilter.class.isAssignableFrom(clazz))
            {
               providers.addMethodInvokerFilter(clazz);
            }
            if (RequestFilter.class.isAssignableFrom(clazz))
            {
               providers.addRequestFilter(clazz);
            }
            if (ResponseFilter.class.isAssignableFrom(clazz))
            {
               providers.addResponseFilter(clazz);
            }
         }
         else
         {
            bind(clazz); // 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
    * 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
    */
   public boolean bind(final Object resource)
   {
      final Path path = resource.getClass().getAnnotation(Path.class);

      AbstractResourceDescriptor descriptor = null;
      if (path != null)
      {
         try
         {
            descriptor = new AbstractResourceDescriptorImpl(resource);
         }
         catch (Exception e)
         {
            String msg = "Unexpected error occurs when process resource class " + resource.getClass().getName();
            LOG.error(msg, e);
            return false;
         }
      }
      else
      {
         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;
      }

      // validate AbstractResourceDescriptor
      try
      {
         descriptor.accept(rdv);
      }
      catch (Exception e)
      {
         LOG.error("Validation of root resource failed. ", e);
         return false;
      }

      synchronized (rootResources)
      {
         // check does exist other resource with the same URI pattern
         for (ObjectFactory<AbstractResourceDescriptor> exist : rootResources)
         {
            if (exist.getObjectModel().getUriPattern().equals(descriptor.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;
            }
         }

         // Singleton resource
         ObjectFactory<AbstractResourceDescriptor> res =
            new SingletonObjectFactory<AbstractResourceDescriptor>(descriptor, resource);
         rootResources.add(res);
         Collections.sort(rootResources, RESOURCE_COMPARATOR);
         LOG.info("Bind new resource " + res.getObjectModel().getUriPattern().getTemplate() + " : "
            + descriptor.getObjectClass());
      }
      size++;
      return true;
   }

   /**
    * @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
    */
   public boolean bind(final Class<?> resourceClass)
   {
      final Path path = resourceClass.getAnnotation(Path.class);

      AbstractResourceDescriptor descriptor = null;
      if (path != null)
      {
         try
         {
            descriptor = new AbstractResourceDescriptorImpl(resourceClass);
         }
         catch (Exception e)
         {
            String msg = "Unexpected error occurs when process resource class " + resourceClass.getName();
            LOG.error(msg, e);
            return false;
         }
      }
      else
      {
         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);
         return false;
      }

      // validate AbstractResourceDescriptor
      try
      {
         descriptor.accept(rdv);
      }
      catch (Exception e)
      {
         LOG.error("Validation of root resource failed. ", e);
         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
    */
   @SuppressWarnings("unchecked")
   public boolean unbind(Class clazz)
   {
      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;
      }
   }

   public boolean unbind(String uriTemplate)
   {
      synchronized (rootResources)
      {
         Iterator<ObjectFactory<AbstractResourceDescriptor>> i = rootResources.iterator();
         while (i.hasNext())
         {
            ObjectFactory<AbstractResourceDescriptor> res = i.next();
            String t = res.getObjectModel().getUriPattern().getTemplate();
            if (t.equals(uriTemplate))
            {
               i.remove();
               LOG.info("Remove ResourceContainer " + res.getObjectModel().getUriPattern().getTemplate());
               size--;
               return true;
            }
         }
         return false;
      }
   }

   /**
    * Clear the list of ResourceContainer description.
    */
   public void clear()
   {
      rootResources.clear();
      size = 0;
   }

   /**
    * @return all registered root resources
    */
   public List<ObjectFactory<AbstractResourceDescriptor>> getResources()
   {
      return rootResources;
   }

   /**
    * @return number of bound resources
    */
   public int getSize()
   {
      return size;
   }

   /**
    * @return all registered root resources
    */
   @Deprecated
   public List<AbstractResourceDescriptor> getRootResources()
   {
      List<AbstractResourceDescriptor> l = new ArrayList<AbstractResourceDescriptor>(rootResources.size());
      synchronized (rootResources)
      {
         for (ObjectFactory<AbstractResourceDescriptor> f : rootResources)
            l.add(f.getObjectModel());
      }
      return l;
   }

}
TOP

Related Classes of org.exoplatform.services.rest.impl.ResourceBinder$ResourceComparator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.