Package org.jclouds.rest.internal

Source Code of org.jclouds.rest.internal.TransformerForRequest

/**
* Licensed to jclouds, Inc. (jclouds) under one or more
* contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  jclouds licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.jclouds.rest.internal;

import static com.google.common.base.Functions.compose;
import static com.google.inject.util.Types.newParameterizedType;
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import static javax.ws.rs.core.MediaType.APPLICATION_XML;

import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.net.URI;
import java.util.Set;

import javax.inject.Inject;
import javax.lang.model.type.NullType;

import org.jclouds.functions.IdentityFunction;
import org.jclouds.functions.OnlyElementOrNull;
import org.jclouds.http.HttpRequest;
import org.jclouds.http.HttpResponse;
import org.jclouds.http.functions.ParseFirstJsonValueNamed;
import org.jclouds.http.functions.ParseJson;
import org.jclouds.http.functions.ParseSax;
import org.jclouds.http.functions.ParseSax.Factory;
import org.jclouds.http.functions.ParseSax.HandlerWithResult;
import org.jclouds.http.functions.ParseURIFromListOrLocationHeaderIf20x;
import org.jclouds.http.functions.ParseXMLWithJAXB;
import org.jclouds.http.functions.ReleasePayloadAndReturn;
import org.jclouds.http.functions.ReturnInputStream;
import org.jclouds.http.functions.ReturnStringIf2xx;
import org.jclouds.http.functions.ReturnTrueIf2xx;
import org.jclouds.http.functions.UnwrapOnlyJsonValue;
import org.jclouds.json.internal.GsonWrapper;
import org.jclouds.reflect.Invocation;
import org.jclouds.rest.InvocationContext;
import org.jclouds.rest.annotations.JAXBResponseParser;
import org.jclouds.rest.annotations.OnlyElement;
import org.jclouds.rest.annotations.ResponseParser;
import org.jclouds.rest.annotations.SelectJson;
import org.jclouds.rest.annotations.Transform;
import org.jclouds.rest.annotations.Unwrap;
import org.jclouds.rest.annotations.XMLResponseParser;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.reflect.Invokable;
import com.google.common.reflect.TypeToken;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.TypeLiteral;

public class TransformerForRequest implements Function<HttpRequest, Function<HttpResponse, ?>> {
   private final ParseSax.Factory parserFactory;
   private final Injector injector;
   private final GetAcceptHeaders getAcceptHeaders;

   @Inject
   TransformerForRequest(Injector injector, Factory parserFactory, GetAcceptHeaders getAcceptHeaders) {
      this.injector = injector;
      this.parserFactory = parserFactory;
      this.getAcceptHeaders = getAcceptHeaders;
   }

   @SuppressWarnings("unchecked")
   @Override
   public Function<HttpResponse, ?> apply(HttpRequest in) {
      GeneratedHttpRequest request = GeneratedHttpRequest.class.cast(in);
      Function<HttpResponse, ?> transformer;
      Class<? extends HandlerWithResult<?>> handler = getSaxResponseParserClassOrNull(request.getInvocation()
            .getInvokable());
      if (handler != null) {
         transformer = parserFactory.create(injector.getInstance(handler));
      } else {
         transformer = getTransformerForMethod(request.getInvocation(), injector);
      }
      if (transformer instanceof InvocationContext<?>) {
         InvocationContext.class.cast(transformer).setContext(request);
      }
      if (request.getInvocation().getInvokable().isAnnotationPresent(Transform.class)) {
         Function<?, ?> wrappingTransformer = injector.getInstance(request.getInvocation().getInvokable()
               .getAnnotation(Transform.class).value());
         if (wrappingTransformer instanceof InvocationContext<?>) {
            ((InvocationContext<?>) wrappingTransformer).setContext(request);
         }
         transformer = compose(Function.class.cast(wrappingTransformer), transformer);
      }
      return transformer;
   }

   private static final TypeToken<ListenableFuture<Boolean>> futureBooleanToken = new TypeToken<ListenableFuture<Boolean>>() {
      private static final long serialVersionUID = 1L;
   };
   private static final TypeToken<ListenableFuture<String>> futureStringToken = new TypeToken<ListenableFuture<String>>() {
      private static final long serialVersionUID = 1L;
   };
   private static final TypeToken<ListenableFuture<Void>> futureVoidToken = new TypeToken<ListenableFuture<Void>>() {
      private static final long serialVersionUID = 1L;
   };
   private static final TypeToken<ListenableFuture<URI>> futureURIToken = new TypeToken<ListenableFuture<URI>>() {
      private static final long serialVersionUID = 1L;
   };
   private static final TypeToken<ListenableFuture<InputStream>> futureInputStreamToken = new TypeToken<ListenableFuture<InputStream>>() {
      private static final long serialVersionUID = 1L;
   };
   private static final TypeToken<ListenableFuture<HttpResponse>> futureHttpResponseToken = new TypeToken<ListenableFuture<HttpResponse>>() {
      private static final long serialVersionUID = 1L;
   };

   @SuppressWarnings("unchecked")
   @VisibleForTesting
   protected Key<? extends Function<HttpResponse, ?>> getParserOrThrowException(Invocation invocation) {
      Invokable<?, ?> invoked = invocation.getInvokable();
      Set<String> acceptHeaders = getAcceptHeaders.apply(invocation);
      ResponseParser annotation = invoked.getAnnotation(ResponseParser.class);
      Class<?> rawReturnType = invoked.getReturnType().getRawType();
      if (annotation == null) {
         if (rawReturnType.equals(void.class) || invoked.getReturnType().equals(futureVoidToken)) {
            return Key.get(ReleasePayloadAndReturn.class);
         } else if (rawReturnType.equals(boolean.class) || rawReturnType.equals(Boolean.class)
               || invoked.getReturnType().equals(futureBooleanToken)) {
            return Key.get(ReturnTrueIf2xx.class);
         } else if (rawReturnType.equals(InputStream.class)
               || invoked.getReturnType().equals(futureInputStreamToken)) {
            return Key.get(ReturnInputStream.class);
         } else if (rawReturnType.equals(HttpResponse.class)
               || invoked.getReturnType().equals(futureHttpResponseToken)) {
            return Key.get(Class.class.cast(IdentityFunction.class));
         } else if (acceptHeaders.contains(APPLICATION_JSON)) {
            return getJsonParserKeyForMethod(invoked);
         } else if (acceptHeaders.contains(APPLICATION_XML) || invoked.isAnnotationPresent(JAXBResponseParser.class)) {
            return getJAXBParserKeyForMethod(invoked);
         } else if (rawReturnType.equals(String.class) || invoked.getReturnType().equals(futureStringToken)) {
            return Key.get(ReturnStringIf2xx.class);
         } else if (rawReturnType.equals(URI.class) || invoked.getReturnType().equals(futureURIToken)) {
            return Key.get(ParseURIFromListOrLocationHeaderIf20x.class);
         } else {
            throw new IllegalStateException("You must specify a ResponseParser annotation on: " + invoked.toString());
         }
      }
      return Key.get(annotation.value());
   }

   @SuppressWarnings("unchecked")
   private static Key<? extends Function<HttpResponse, ?>> getJAXBParserKeyForMethod(Invokable<?, ?> invoked) {
      Optional<Type> configuredReturnVal = Optional.absent();
      if (invoked.isAnnotationPresent(JAXBResponseParser.class)) {
         Type configuredClass = invoked.getAnnotation(JAXBResponseParser.class).value();
         configuredReturnVal = configuredClass.equals(NullType.class) ? Optional.<Type> absent() : Optional
               .<Type> of(configuredClass);
      }
      Type returnVal = configuredReturnVal.or(getReturnTypeFor(invoked.getReturnType()));
      Type parserType = newParameterizedType(ParseXMLWithJAXB.class, returnVal);
      return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
   }

   @SuppressWarnings({ "unchecked" })
   private static Key<? extends Function<HttpResponse, ?>> getJsonParserKeyForMethod(Invokable<?, ?> invoked) {
      ParameterizedType parserType;
      if (invoked.isAnnotationPresent(Unwrap.class)) {
         parserType = newParameterizedType(UnwrapOnlyJsonValue.class, getReturnTypeFor(invoked.getReturnType()));
      } else {
         parserType = newParameterizedType(ParseJson.class, getReturnTypeFor(invoked.getReturnType()));
      }
      return (Key<? extends Function<HttpResponse, ?>>) Key.get(parserType);
   }

   static Type getReturnTypeFor(TypeToken<?> typeToken) {
      Type returnVal = typeToken.getType();
      if (typeToken.getRawType().getTypeParameters().length == 0) {
         returnVal = typeToken.getRawType();
      } else if (typeToken.getRawType().equals(ListenableFuture.class)) {
         ParameterizedType futureType = (ParameterizedType) typeToken.getType();
         returnVal = futureType.getActualTypeArguments()[0];
         if (returnVal instanceof WildcardType)
            returnVal = WildcardType.class.cast(returnVal).getUpperBounds()[0];
      }
      return returnVal;
   }

   // TODO: refactor this out of here
   @VisibleForTesting
   @SuppressWarnings({ "rawtypes", "unchecked" })
   public Function<HttpResponse, ?> getTransformerForMethod(Invocation invocation, Injector injector) {
      Invokable<?, ?> invoked = invocation.getInvokable();
      Function<HttpResponse, ?> transformer;
      if (invoked.isAnnotationPresent(SelectJson.class)) {
         Type returnVal = getReturnTypeFor(invoked.getReturnType());
         if (invoked.isAnnotationPresent(OnlyElement.class))
            returnVal = newParameterizedType(Set.class, returnVal);
         transformer = new ParseFirstJsonValueNamed(injector.getInstance(GsonWrapper.class),
               TypeLiteral.get(returnVal), invoked.getAnnotation(SelectJson.class).value());
         if (invoked.isAnnotationPresent(OnlyElement.class))
            transformer = compose(new OnlyElementOrNull(), transformer);
      } else {
         transformer = injector.getInstance(getParserOrThrowException(invocation));
      }
      return transformer;
   }

   static Class<? extends HandlerWithResult<?>> getSaxResponseParserClassOrNull(Invokable<?, ?> invoked) {
      XMLResponseParser annotation = invoked.getAnnotation(XMLResponseParser.class);
      if (annotation != null) {
         return annotation.value();
      }
      return null;
   }
}
TOP

Related Classes of org.jclouds.rest.internal.TransformerForRequest

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.