Package org.gatein.management.core.spi

Source Code of org.gatein.management.core.spi.AnnotatedOperation

/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* 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.gatein.management.core.spi;

import org.gatein.common.logging.Logger;
import org.gatein.common.logging.LoggerFactory;
import org.gatein.management.api.ExternalContext;
import org.gatein.management.api.ManagedUser;
import org.gatein.management.api.PathAddress;
import org.gatein.management.api.RuntimeContext;
import org.gatein.management.api.annotations.Managed;
import org.gatein.management.api.annotations.ManagedContext;
import org.gatein.management.api.annotations.ManagedOperation;
import org.gatein.management.api.annotations.ManagedRole;
import org.gatein.management.api.annotations.MappedAttribute;
import org.gatein.management.api.annotations.MappedPath;
import org.gatein.management.api.binding.Marshaller;
import org.gatein.management.api.exceptions.InvalidDataException;
import org.gatein.management.api.exceptions.NotAuthorizedException;
import org.gatein.management.api.exceptions.OperationException;
import org.gatein.management.api.exceptions.ResourceExistsException;
import org.gatein.management.api.exceptions.ResourceNotFoundException;
import org.gatein.management.api.model.ModelProvider;
import org.gatein.management.api.model.ModelValue;
import org.gatein.management.api.operation.OperationAttachment;
import org.gatein.management.api.operation.OperationAttributes;
import org.gatein.management.api.operation.OperationContext;
import org.gatein.management.api.operation.OperationHandler;
import org.gatein.management.api.operation.OperationNames;
import org.gatein.management.api.operation.ResultHandler;
import org.gatein.management.api.operation.model.NoResultModel;
import org.gatein.management.core.api.AbstractManagedResource;
import org.gatein.management.core.api.model.DmrModelValue;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.util.List;

import static org.gatein.management.core.spi.AnnotatedResource.*;

/**
* @author <a href="mailto:nscavell@redhat.com">Nick Scavelli</a>
*/
class AnnotatedOperation implements OperationHandler
{
   private static final Logger log = LoggerFactory.getLogger("org.gatein.management.core.spi");

   private final AnnotatedResource owner;
   final Method method;
   private final String methodName;
   private final String managedRole;

   public AnnotatedOperation(AnnotatedResource owner, Method method)
   {
      this.owner = owner;
      this.method = method;
      this.methodName = getName();
      ManagedRole role = method.getAnnotation(ManagedRole.class);
      managedRole = (role == null) ? null : role.value();
   }

   public void registerOperation(AbstractManagedResource managedResource)
   {
      final AbstractManagedResource resource = registerOrGetResource(managedResource, method.getAnnotation(Managed.class));
      ManagedOperation mo = method.getAnnotation(ManagedOperation.class);
      String operationName = OperationNames.READ_RESOURCE;
      String desc = "";
      if (mo != null)
      {
         operationName = mo.name();
         desc = mo.description();
      }

      final boolean debug = log.isDebugEnabled();
      if (debug) log.debug("Registering operation " + operationName + " for path " + resource.getPath());

      Class<?> returnType = method.getReturnType();
      Managed subManaged = returnType.getAnnotation(Managed.class);
      if (subManaged != null)
      {
         if ("".equals(subManaged.value()))
         {
            AnnotatedResource ar = new AnnotatedResource(returnType, owner, this);
            ar.register(resource);
         }
         else
         {
            throw new RuntimeException("Cannot register method " + methodName + " for class " + owner.managedClass.getName()
               + " because return type " + returnType.getName() + " is annotated with a value for the @" + Managed.class.getSimpleName() + " annotation.");
         }
      }
      else
      {
         resource.registerOperationHandler(operationName, this, description(desc));
      }
   }

   @Override
   public void execute(OperationContext operationContext, ResultHandler resultHandler) throws ResourceNotFoundException, OperationException
   {
      if (log.isDebugEnabled())
      {
         log.debug(String.format("Executing operation handler for annotated method %s for address %s", methodName, operationContext.getAddress()));
      }

      // Make sure user is authorized to invoke operation
      if (!isAuthorized(operationContext.getExternalContext(), managedRole, owner.managedRole))
      {
         throw new NotAuthorizedException(operationContext.getUser(), operationContext.getOperationName());
      }

      invokeBefore(operationContext);
      try
      {
         Object result = invokeOperation(operationContext);
         if (method.getReturnType() == void.class)
         {
            resultHandler.completed(NoResultModel.INSTANCE);
         }
         else
         {
            if (result == null)
            {
               log.error("Result returned was null and method " + methodName + " for managed component " + owner.managedClass + " is not void.");
               throw new ResourceNotFoundException("Resource not found.");
            }
            else
            {
               resultHandler.completed(result);
            }
         }
      }
      finally
      {
         invokeAfter(operationContext);
      }
   }

   Object invokeOperation(OperationContext context)
   {
      return invokeMethod(context, owner.getInstance(context),  method);
   }

   private void invokeBefore(OperationContext context)
   {
      if (owner.parent != null && owner.operation != null)
      {
         owner.operation.invokeBefore(context);
      }

      Object instance = owner.getInstance(context);
      if (owner.beforeMethod != null && instance != null)
      {
         invokeMethod(context, instance, owner.beforeMethod);
      }
   }

   private void invokeAfter(OperationContext context)
   {
      Object instance = owner.getInstance(context);
      if (owner.afterMethod != null && instance != null)
      {
         invokeMethod(context, instance, owner.afterMethod);
      }
      owner.discardInstance();

      if (owner.parent != null && owner.operation != null)
      {
         owner.operation.invokeAfter(context);
      }
   }

   private Object invokeMethod(OperationContext context, Object instance, Method method)
   {
      if (method == null || instance == null) return null;

      Object[] params = getParameters(context, method, getName(method), owner.managedClass);
      try
      {
         return method.invoke(instance, params);
      }
      catch (IllegalAccessException e)
      {
         throw new RuntimeException("Cannot access method " + this.method + " on object " + instance, e);
      }
      catch (InvocationTargetException e)
      {
         if (e.getCause() instanceof ResourceNotFoundException)
         {
            throw (ResourceNotFoundException) e.getCause();
         }
         else if (e.getCause() instanceof ResourceExistsException)
         {
            throw (ResourceExistsException) e.getCause();
         }
         else if (e.getCause() instanceof OperationException)
         {
            throw (OperationException) e.getCause();
         }
         else if (e.getCause() instanceof InvalidDataException)
         {
            throw (InvalidDataException) e.getCause();
         }
         throw new RuntimeException("Could not invoke method " + this.method + " on object " + instance, e);
      }
   }

   private static boolean isAuthorized(ExternalContext context, String operationRole, String resourceRole)
   {
      if (operationRole != null)
      {
         return context.isUserInRole(operationRole);
      }

      return resourceRole == null || context.isUserInRole(resourceRole);
   }

   private static Object[] getParameters(OperationContext operationContext, Method method, String methodName, Class<?> managedClass)
   {
      boolean debug = log.isDebugEnabled();
      String operationName = operationContext.getOperationName();
      Annotation[][] parameterAnnotations = method.getParameterAnnotations();
      Object[] params = new Object[parameterAnnotations.length];
      OperationAttachment attachment = null;
      for (int i = 0; i < parameterAnnotations.length; i++)
      {
         MappedPath pathTemplate;
         MappedAttribute managedAttribute;
         // Resolve path template and set as parameter to method
         if ((pathTemplate = getAnnotation(parameterAnnotations[i], MappedPath.class)) != null)
         {
            params[i] = operationContext.getAddress().resolvePathTemplate(pathTemplate.value());
            if (debug) log.debug("Resolved path template " + pathTemplate.value() + "=" + params[i]);
         }
         // Resolve attribute name and set as parameter to method
         else if ((managedAttribute = getAnnotation(parameterAnnotations[i], MappedAttribute.class)) != null)
         {
            if (List.class == method.getParameterTypes()[i])
            {
               params[i] = operationContext.getAttributes().getValues(managedAttribute.value());
            }
            else if (String.class == method.getParameterTypes()[i])
            {
               params[i] = operationContext.getAttributes().getValue(managedAttribute.value());
            }
            else
            {
               throw new RuntimeException("The parameter type " + method.getParameterTypes()[i] +
                  " cannot be annotated by @" + MappedAttribute.class.getName() + ". Only List<String> and String are allowed.");
            }

            if (debug) log.debug("Resolved attribute " + managedAttribute.value() + "=" + params[i]);
         }
         // Method wants something from the OperationContext, or the entire OperationContext object.
         else if ((getAnnotation(parameterAnnotations[i], ManagedContext.class)) != null)
         {
            Class<?> parameterType = method.getParameterTypes()[i];

            if (RuntimeContext.class == parameterType)
            {
               params[i] = operationContext.getRuntimeContext();
            }
            else if (PathAddress.class == parameterType)
            {
               params[i] = operationContext.getAddress();
            }
            else if (OperationAttributes.class == parameterType)
            {
               params[i] = operationContext.getAttributes();
            }
            else if (ManagedUser.class == parameterType)
            {
               params[i] = operationContext.getUser();
            }
            else if (ModelValue.class.isAssignableFrom(parameterType))
            {
               if ( (attachment = operationContext.getAttachment(true)) != null)
               {
                  try
                  {
//                     StringBuilder sb = new StringBuilder();
//                     InputStream in = attachment.getStream();
//                     byte[] buf = new byte[2048];
//                     while ( (in.read(buf)) != -1) {
//                        sb.append(new String(buf, Charset.forName("US-ASCII")));
//                     }
//                     String json = sb.toString();
//                     ByteArrayInputStream bais = new ByteArrayInputStream(json.getBytes());
//                     System.out.println("[\n"+json+"\n]");
                     params[i] = DmrModelValue.readFromJsonStream(attachment.getStream());
                  }
                  catch (IOException e)
                  {
                     log.error("IOException reading from JSON stream for detyped model.", e);
                     throw new OperationException(operationName, "Could not properly read data stream. See log for more details.", e);
                  }
               }
               else
               {
                  throw new OperationException(operationName, "Data stream not available.");
               }
            }
            else if (ModelProvider.class.isAssignableFrom(parameterType))
            {
               @SuppressWarnings("unchecked")
               Class<? extends ModelValue> type = (Class<? extends ModelValue>) parameterType;
               params[i] = operationContext.newModel(type);
            }
            else if (OperationContext.class == parameterType)
            {
               params[i] = operationContext;
            }
         }
         else
         {
            Class<?> marshalClass = method.getParameterTypes()[i];
            if (debug) log.debug("Encountered unannotated parameter. Will try and find marshaller for type " + marshalClass);

            // Currently only one attachment is supported, and that's the data stream (input) of the management operation.
            if (attachment != null)
            {
               throw new RuntimeException("Cannot unmarshal " + marshalClass + " for method " + methodName +
                  " and component " + managedClass.getName() + ". This is because input stream was already consumed. " +
                  "This can happen if the marshaled type is not declared before @ManagedContext for detyped ModelValue type.");
            }
            Marshaller<?> marshaller = operationContext.getBindingProvider().getMarshaller(marshalClass, operationContext.getContentType());
            if (marshaller != null)
            {
               attachment = operationContext.getAttachment(true);
               if (attachment == null) throw new OperationException(operationName, "No attachment was found for this operation.");

               params[i] = marshaller.unmarshal(attachment.getStream());
               if (debug) log.debug("Successfully unmarshaled object of type " + marshalClass);
            }
            else
            {
               throw new RuntimeException("Could not find marshaller for " + marshalClass +
                  " and therefore cannot pass parameter of this type to method " + methodName + " for component " + managedClass.getName());
            }
         }
      }
      return params;
   }

   private String getName()
   {
      return getName(method);
   }

   private static String getName(Method method)
   {
      String name = method.getName();
      StringBuilder sb = new StringBuilder();
      sb.append(name).append("(");
      Class<?>[] parameters = method.getParameterTypes();
      for (int i=0; i<parameters.length; i++)
      {
         sb.append(parameters[i].getName());
         if (i != parameters.length-1)
         {
            sb.append(", ");
         }
      }
      sb.append(")");

      return sb.toString();
   }

   private static <A extends Annotation> A getAnnotation(Annotation[] annotations, Class<A> type)
   {
      for (Annotation annotation : annotations)
      {
         if (annotation.annotationType() == type) return type.cast(annotation);
      }

      return null;
   }
}
TOP

Related Classes of org.gatein.management.core.spi.AnnotatedOperation

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.