예제 #1
0
public final class ResourceUtils {

  private static final Logger LOG = LogUtils.getL7dLogger(ResourceUtils.class);
  private static final ResourceBundle BUNDLE = BundleUtils.getBundle(ResourceUtils.class);
  private static final String CLASSPATH_PREFIX = "classpath:";
  private static final Set<String> SERVER_PROVIDER_CLASS_NAMES;

  static {
    SERVER_PROVIDER_CLASS_NAMES = new HashSet<String>();
    SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.MessageBodyWriter");
    SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.MessageBodyReader");
    SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.ExceptionMapper");
    SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.ContextResolver");
    SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.ReaderInterceptor");
    SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.WriterInterceptor");
    SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.ext.ParamConverterProvider");
    SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.container.ContainerRequestFilter");
    SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.container.ContainerResponseFilter");
    SERVER_PROVIDER_CLASS_NAMES.add("javax.ws.rs.container.DynamicFeature");
    SERVER_PROVIDER_CLASS_NAMES.add("org.apache.cxf.jaxrs.ext.ContextResolver");
  }

  private ResourceUtils() {}

  public static Method findPostConstructMethod(Class<?> c) {
    return findPostConstructMethod(c, null);
  }

  public static Method findPostConstructMethod(Class<?> c, String name) {
    if (Object.class == c || null == c) {
      return null;
    }
    for (Method m : c.getDeclaredMethods()) {
      if (name != null) {
        if (m.getName().equals(name)) {
          return m;
        }
      } else if (m.getAnnotation(PostConstruct.class) != null) {
        return m;
      }
    }
    Method m = findPostConstructMethod(c.getSuperclass(), name);
    if (m != null) {
      return m;
    }
    for (Class<?> i : c.getInterfaces()) {
      m = findPostConstructMethod(i, name);
      if (m != null) {
        return m;
      }
    }
    return null;
  }

  public static Method findPreDestroyMethod(Class<?> c) {
    return findPreDestroyMethod(c, null);
  }

  public static Method findPreDestroyMethod(Class<?> c, String name) {
    if (Object.class == c || null == c) {
      return null;
    }
    for (Method m : c.getDeclaredMethods()) {
      if (name != null) {
        if (m.getName().equals(name)) {
          return m;
        }
      } else if (m.getAnnotation(PreDestroy.class) != null) {
        return m;
      }
    }
    Method m = findPreDestroyMethod(c.getSuperclass(), name);
    if (m != null) {
      return m;
    }
    for (Class<?> i : c.getInterfaces()) {
      m = findPreDestroyMethod(i, name);
      if (m != null) {
        return m;
      }
    }
    return null;
  }

  public static ClassResourceInfo createClassResourceInfo(
      Map<String, UserResource> resources,
      UserResource model,
      Class<?> defaultClass,
      boolean isRoot,
      boolean enableStatic,
      Bus bus) {
    final boolean isDefaultClass = defaultClass != null;
    Class<?> sClass = !isDefaultClass ? loadClass(model.getName()) : defaultClass;
    return createServiceClassResourceInfo(resources, model, sClass, isRoot, enableStatic, bus);
  }

  public static ClassResourceInfo createServiceClassResourceInfo(
      Map<String, UserResource> resources,
      UserResource model,
      Class<?> sClass,
      boolean isRoot,
      boolean enableStatic,
      Bus bus) {
    if (model == null) {
      throw new RuntimeException("Resource class " + sClass.getName() + " has no model info");
    }
    ClassResourceInfo cri =
        new ClassResourceInfo(
            sClass,
            sClass,
            isRoot,
            enableStatic,
            true,
            model.getConsumes(),
            model.getProduces(),
            bus);
    URITemplate t = URITemplate.createTemplate(model.getPath());
    cri.setURITemplate(t);

    MethodDispatcher md = new MethodDispatcher();
    Map<String, UserOperation> ops = model.getOperationsAsMap();

    Method defaultMethod = null;
    Map<String, Method> methodNames = new HashMap<String, Method>();
    for (Method m : cri.getServiceClass().getMethods()) {
      if (m.getAnnotation(DefaultMethod.class) != null) {
        // if needed we can also support multiple default methods
        defaultMethod = m;
      }
      methodNames.put(m.getName(), m);
    }

    for (Map.Entry<String, UserOperation> entry : ops.entrySet()) {
      UserOperation op = entry.getValue();
      Method actualMethod = methodNames.get(op.getName());
      if (actualMethod == null) {
        actualMethod = defaultMethod;
      }
      if (actualMethod == null) {
        continue;
      }
      OperationResourceInfo ori =
          new OperationResourceInfo(
              actualMethod,
              cri,
              URITemplate.createTemplate(op.getPath()),
              op.getVerb(),
              op.getConsumes(),
              op.getProduces(),
              op.getParameters(),
              op.isOneway());
      String rClassName = actualMethod.getReturnType().getName();
      if (op.getVerb() == null) {
        if (resources.containsKey(rClassName)) {
          ClassResourceInfo subCri =
              rClassName.equals(model.getName())
                  ? cri
                  : createServiceClassResourceInfo(
                      resources,
                      resources.get(rClassName),
                      actualMethod.getReturnType(),
                      false,
                      enableStatic,
                      bus);
          if (subCri != null) {
            cri.addSubClassResourceInfo(subCri);
            md.bind(ori, actualMethod);
          }
        }
      } else {
        md.bind(ori, actualMethod);
      }
    }

    cri.setMethodDispatcher(md);
    return checkMethodDispatcher(cri) ? cri : null;
  }

  public static ClassResourceInfo createClassResourceInfo(
      final Class<?> rClass, final Class<?> sClass, boolean root, boolean enableStatic) {
    return createClassResourceInfo(
        rClass, sClass, root, enableStatic, BusFactory.getThreadDefaultBus());
  }

  public static ClassResourceInfo createClassResourceInfo(
      final Class<?> rClass, final Class<?> sClass, boolean root, boolean enableStatic, Bus bus) {
    return createClassResourceInfo(rClass, sClass, null, root, enableStatic, bus);
  }

  public static ClassResourceInfo createClassResourceInfo(
      final Class<?> rClass,
      final Class<?> sClass,
      ClassResourceInfo parent,
      boolean root,
      boolean enableStatic,
      Bus bus) {
    ClassResourceInfo cri = new ClassResourceInfo(rClass, sClass, root, enableStatic, bus);
    cri.setParent(parent);

    if (root) {
      URITemplate t = URITemplate.createTemplate(cri.getPath());
      cri.setURITemplate(t);
    }

    evaluateResourceClass(cri, enableStatic);
    return checkMethodDispatcher(cri) ? cri : null;
  }

  private static void evaluateResourceClass(ClassResourceInfo cri, boolean enableStatic) {
    MethodDispatcher md = new MethodDispatcher();
    Class<?> serviceClass = cri.getServiceClass();

    boolean isFineLevelLoggable = LOG.isLoggable(Level.FINE);
    for (Method m : serviceClass.getMethods()) {

      Method annotatedMethod = AnnotationUtils.getAnnotatedMethod(serviceClass, m);

      String httpMethod = AnnotationUtils.getHttpMethodValue(annotatedMethod);
      Path path = AnnotationUtils.getMethodAnnotation(annotatedMethod, Path.class);

      if (httpMethod != null || path != null) {
        md.bind(createOperationInfo(m, annotatedMethod, cri, path, httpMethod), m);
        if (httpMethod == null) {
          // subresource locator
          Class<?> subClass = m.getReturnType();
          if (enableStatic) {
            ClassResourceInfo subCri = cri.findResource(subClass, subClass);
            if (subCri == null) {
              ClassResourceInfo ancestor = getAncestorWithSameServiceClass(cri, subClass);
              subCri =
                  ancestor != null
                      ? ancestor
                      : createClassResourceInfo(
                          subClass, subClass, cri, false, enableStatic, cri.getBus());
            }

            if (subCri != null) {
              cri.addSubClassResourceInfo(subCri);
            }
          }
        }
      } else if (isFineLevelLoggable) {
        LOG.fine(
            new org.apache.cxf.common.i18n.Message(
                    "NOT_RESOURCE_METHOD", BUNDLE, m.getDeclaringClass().getName(), m.getName())
                .toString());
      }
    }
    cri.setMethodDispatcher(md);
  }

  private static ClassResourceInfo getAncestorWithSameServiceClass(
      ClassResourceInfo parent, Class<?> subClass) {
    if (parent == null) {
      return null;
    }
    if (parent.getServiceClass() == subClass) {
      return parent;
    }
    return getAncestorWithSameServiceClass(parent.getParent(), subClass);
  }

  public static Constructor<?> findResourceConstructor(Class<?> resourceClass, boolean perRequest) {
    List<Constructor<?>> cs = new LinkedList<Constructor<?>>();
    for (Constructor<?> c : resourceClass.getConstructors()) {
      Class<?>[] params = c.getParameterTypes();
      Annotation[][] anns = c.getParameterAnnotations();
      boolean match = true;
      for (int i = 0; i < params.length; i++) {
        if (!perRequest) {
          if (AnnotationUtils.getAnnotation(anns[i], Context.class) == null) {
            match = false;
            break;
          }
        } else if (!AnnotationUtils.isValidParamAnnotations(anns[i])) {
          match = false;
          break;
        }
      }
      if (match) {
        cs.add(c);
      }
    }
    Collections.sort(
        cs,
        new Comparator<Constructor<?>>() {

          public int compare(Constructor<?> c1, Constructor<?> c2) {
            int p1 = c1.getParameterTypes().length;
            int p2 = c2.getParameterTypes().length;
            return p1 > p2 ? -1 : p1 < p2 ? 1 : 0;
          }
        });
    return cs.size() == 0 ? null : cs.get(0);
  }

  public static List<Parameter> getParameters(Method resourceMethod) {
    Annotation[][] paramAnns = resourceMethod.getParameterAnnotations();
    if (paramAnns.length == 0) {
      return CastUtils.cast(Collections.emptyList(), Parameter.class);
    }
    Class<?>[] types = resourceMethod.getParameterTypes();
    List<Parameter> params = new ArrayList<Parameter>(paramAnns.length);
    for (int i = 0; i < paramAnns.length; i++) {
      Parameter p = getParameter(i, paramAnns[i], types[i]);
      params.add(p);
    }
    return params;
  }

  // CHECKSTYLE:OFF
  public static Parameter getParameter(int index, Annotation[] anns, Class<?> type) {

    Context ctx = AnnotationUtils.getAnnotation(anns, Context.class);
    if (ctx != null) {
      return new Parameter(ParameterType.CONTEXT, index, null);
    }

    boolean isEncoded = AnnotationUtils.getAnnotation(anns, Encoded.class) != null;

    BeanParam bp = AnnotationUtils.getAnnotation(anns, BeanParam.class);
    if (bp != null) {
      return new Parameter(ParameterType.BEAN, index, null, isEncoded, null);
    }

    String dValue = AnnotationUtils.getDefaultParameterValue(anns);

    PathParam a = AnnotationUtils.getAnnotation(anns, PathParam.class);
    if (a != null) {
      return new Parameter(ParameterType.PATH, index, a.value(), isEncoded, dValue);
    }
    QueryParam q = AnnotationUtils.getAnnotation(anns, QueryParam.class);
    if (q != null) {
      return new Parameter(ParameterType.QUERY, index, q.value(), isEncoded, dValue);
    }
    MatrixParam m = AnnotationUtils.getAnnotation(anns, MatrixParam.class);
    if (m != null) {
      return new Parameter(ParameterType.MATRIX, index, m.value(), isEncoded, dValue);
    }

    FormParam f = AnnotationUtils.getAnnotation(anns, FormParam.class);
    if (f != null) {
      return new Parameter(ParameterType.FORM, index, f.value(), isEncoded, dValue);
    }

    HeaderParam h = AnnotationUtils.getAnnotation(anns, HeaderParam.class);
    if (h != null) {
      return new Parameter(ParameterType.HEADER, index, h.value(), isEncoded, dValue);
    }

    CookieParam c = AnnotationUtils.getAnnotation(anns, CookieParam.class);
    if (c != null) {
      return new Parameter(ParameterType.COOKIE, index, c.value(), isEncoded, dValue);
    }

    return new Parameter(ParameterType.REQUEST_BODY, index, null);
  }
  // CHECKSTYLE:ON

  private static OperationResourceInfo createOperationInfo(
      Method m, Method annotatedMethod, ClassResourceInfo cri, Path path, String httpMethod) {
    OperationResourceInfo ori = new OperationResourceInfo(m, annotatedMethod, cri);
    URITemplate t = URITemplate.createTemplate(path);
    ori.setURITemplate(t);
    ori.setHttpMethod(httpMethod);
    return ori;
  }

  private static boolean checkMethodDispatcher(ClassResourceInfo cr) {
    if (cr.getMethodDispatcher().getOperationResourceInfos().isEmpty()) {
      LOG.warning(
          new org.apache.cxf.common.i18n.Message(
                  "NO_RESOURCE_OP_EXC", BUNDLE, cr.getServiceClass().getName())
              .toString());
      return false;
    }
    return true;
  }

  private static Class<?> loadClass(String cName) {
    try {
      return ClassLoaderUtils.loadClass(cName.trim(), ResourceUtils.class);
    } catch (ClassNotFoundException ex) {
      throw new RuntimeException("No class " + cName.trim() + " can be found", ex);
    }
  }

  public static List<UserResource> getUserResources(String loc, Bus bus) {
    try {
      InputStream is = ResourceUtils.getResourceStream(loc, bus);
      if (is == null) {
        return null;
      }
      return getUserResources(is);
    } catch (Exception ex) {
      LOG.warning("Problem with processing a user model at " + loc);
    }

    return null;
  }

  public static InputStream getResourceStream(String loc, Bus bus) throws Exception {
    URL url = getResourceURL(loc, bus);
    return url == null ? null : url.openStream();
  }

  public static URL getResourceURL(String loc, Bus bus) throws Exception {
    URL url = null;
    if (loc.startsWith(CLASSPATH_PREFIX)) {
      String path = loc.substring(CLASSPATH_PREFIX.length());
      url = ResourceUtils.getClasspathResourceURL(path, ResourceUtils.class, bus);
    } else {
      try {
        url = new URL(loc);
      } catch (Exception ex) {
        // it can be either a classpath or file resource without a scheme
        url = ResourceUtils.getClasspathResourceURL(loc, ResourceUtils.class, bus);
        if (url == null) {
          File file = new File(loc);
          if (file.exists()) {
            url = file.toURI().toURL();
          }
        }
      }
    }
    if (url == null) {
      LOG.warning("No resource " + loc + " is available");
    }
    return url;
  }

  public static InputStream getClasspathResourceStream(
      String path, Class<?> callingClass, Bus bus) {
    InputStream is = ClassLoaderUtils.getResourceAsStream(path, callingClass);
    return is == null ? getResource(path, InputStream.class, bus) : is;
  }

  public static URL getClasspathResourceURL(String path, Class<?> callingClass, Bus bus) {
    URL url = ClassLoaderUtils.getResource(path, callingClass);
    return url == null ? getResource(path, URL.class, bus) : url;
  }

  public static <T> T getResource(String path, Class<T> resourceClass, Bus bus) {
    if (bus != null) {
      ResourceManager rm = bus.getExtension(ResourceManager.class);
      if (rm != null) {
        return rm.resolveResource(path, resourceClass);
      }
    }
    return null;
  }

  public static Properties loadProperties(String propertiesLocation, Bus bus) throws Exception {
    Properties props = new Properties();
    InputStream is = getResourceStream(propertiesLocation, bus);
    props.load(is);
    return props;
  }

  public static List<UserResource> getUserResources(String loc) {
    return getUserResources(loc, BusFactory.getThreadDefaultBus());
  }

  public static List<UserResource> getUserResources(InputStream is) throws Exception {
    Document doc = StaxUtils.read(new InputStreamReader(is, "UTF-8"));
    return getResourcesFromElement(doc.getDocumentElement());
  }

  public static List<UserResource> getResourcesFromElement(Element modelEl) {
    List<UserResource> resources = new ArrayList<UserResource>();
    List<Element> resourceEls =
        DOMUtils.findAllElementsByTagNameNS(modelEl, "http://cxf.apache.org/jaxrs", "resource");
    for (Element e : resourceEls) {
      resources.add(getResourceFromElement(e));
    }
    return resources;
  }

  public static ResourceTypes getAllRequestResponseTypes(
      List<ClassResourceInfo> cris, boolean jaxbOnly) {
    return getAllRequestResponseTypes(cris, jaxbOnly, null);
  }

  public static ResourceTypes getAllRequestResponseTypes(
      List<ClassResourceInfo> cris, boolean jaxbOnly, MessageBodyWriter<?> jaxbWriter) {
    ResourceTypes types = new ResourceTypes();
    for (ClassResourceInfo resource : cris) {
      getAllTypesForResource(resource, types, jaxbOnly, jaxbWriter);
    }
    return types;
  }

  public static Class<?> getActualJaxbType(Class<?> type, Method resourceMethod, boolean inbound) {
    ElementClass element = resourceMethod.getAnnotation(ElementClass.class);
    if (element != null) {
      Class<?> cls = inbound ? element.request() : element.response();
      if (cls != Object.class) {
        return cls;
      }
    }
    return type;
  }

  private static void getAllTypesForResource(
      ClassResourceInfo resource,
      ResourceTypes types,
      boolean jaxbOnly,
      MessageBodyWriter<?> jaxbWriter) {
    for (OperationResourceInfo ori : resource.getMethodDispatcher().getOperationResourceInfos()) {
      Method method = ori.getMethodToInvoke();
      Class<?> realReturnType = method.getReturnType();
      Class<?> cls = realReturnType;
      if (cls == Response.class) {
        cls = getActualJaxbType(cls, method, false);
      }
      Type type = method.getGenericReturnType();
      if (jaxbOnly) {
        checkJaxbType(
            resource.getServiceClass(),
            cls,
            realReturnType == Response.class ? cls : type,
            types,
            method.getAnnotations(),
            jaxbWriter);
      } else {
        types.getAllTypes().put(cls, type);
      }

      for (Parameter pm : ori.getParameters()) {
        if (pm.getType() == ParameterType.REQUEST_BODY) {
          Class<?> inType = method.getParameterTypes()[pm.getIndex()];
          Type paramType = method.getGenericParameterTypes()[pm.getIndex()];
          if (jaxbOnly) {
            checkJaxbType(
                resource.getServiceClass(),
                inType,
                paramType,
                types,
                method.getParameterAnnotations()[pm.getIndex()],
                jaxbWriter);
          } else {
            types.getAllTypes().put(inType, paramType);
          }
        }
      }
    }

    for (ClassResourceInfo sub : resource.getSubResources()) {
      if (!isRecursiveSubResource(resource, sub)) {
        getAllTypesForResource(sub, types, jaxbOnly, jaxbWriter);
      }
    }
  }

  private static boolean isRecursiveSubResource(ClassResourceInfo parent, ClassResourceInfo sub) {
    if (parent == null) {
      return false;
    }
    if (parent == sub) {
      return true;
    }
    return isRecursiveSubResource(parent.getParent(), sub);
  }

  private static void checkJaxbType(
      Class<?> serviceClass,
      Class<?> type,
      Type genericType,
      ResourceTypes types,
      Annotation[] anns,
      MessageBodyWriter<?> jaxbWriter) {
    boolean isCollection = false;
    if (InjectionUtils.isSupportedCollectionOrArray(type)) {
      type = InjectionUtils.getActualType(genericType);
      isCollection = true;
    }
    if (type == Object.class && !(genericType instanceof Class)) {
      Type theType =
          InjectionUtils.processGenericTypeIfNeeded(serviceClass, Object.class, genericType);
      type = InjectionUtils.getActualType(theType);
    }
    if (type == null
        || InjectionUtils.isPrimitive(type)
        || JAXBElement.class.isAssignableFrom(type)
        || Response.class.isAssignableFrom(type)
        || type.isInterface()) {
      return;
    }

    MessageBodyWriter<?> writer = jaxbWriter;
    if (writer == null) {
      JAXBElementProvider<Object> defaultWriter = new JAXBElementProvider<Object>();
      defaultWriter.setMarshallAsJaxbElement(true);
      defaultWriter.setXmlTypeAsJaxbElementOnly(true);
      writer = defaultWriter;
    }
    if (writer.isWriteable(type, type, anns, MediaType.APPLICATION_XML_TYPE)) {
      types.getAllTypes().put(type, type);
      Class<?> genCls = InjectionUtils.getActualType(genericType);
      if (genCls != type
          && genCls != null
          && genCls != Object.class
          && !InjectionUtils.isSupportedCollectionOrArray(genCls)) {
        types.getAllTypes().put(genCls, genCls);
      }

      XMLName name = AnnotationUtils.getAnnotation(anns, XMLName.class);
      QName qname = name != null ? JAXRSUtils.convertStringToQName(name.value()) : null;
      if (isCollection) {
        types.getCollectionMap().put(type, qname);
      } else {
        types.getXmlNameMap().put(type, qname);
      }
    }
  }

  private static UserResource getResourceFromElement(Element e) {
    UserResource resource = new UserResource();
    resource.setName(e.getAttribute("name"));
    resource.setPath(e.getAttribute("path"));
    resource.setConsumes(e.getAttribute("consumes"));
    resource.setProduces(e.getAttribute("produces"));
    List<Element> operEls =
        DOMUtils.findAllElementsByTagNameNS(e, "http://cxf.apache.org/jaxrs", "operation");
    List<UserOperation> opers = new ArrayList<UserOperation>(operEls.size());
    for (Element operEl : operEls) {
      opers.add(getOperationFromElement(operEl));
    }
    resource.setOperations(opers);
    return resource;
  }

  private static UserOperation getOperationFromElement(Element e) {
    UserOperation op = new UserOperation();
    op.setName(e.getAttribute("name"));
    op.setVerb(e.getAttribute("verb"));
    op.setPath(e.getAttribute("path"));
    op.setOneway(Boolean.parseBoolean(e.getAttribute("oneway")));
    op.setConsumes(e.getAttribute("consumes"));
    op.setProduces(e.getAttribute("produces"));
    List<Element> paramEls =
        DOMUtils.findAllElementsByTagNameNS(e, "http://cxf.apache.org/jaxrs", "param");
    List<Parameter> params = new ArrayList<Parameter>(paramEls.size());
    for (int i = 0; i < paramEls.size(); i++) {
      Element paramEl = paramEls.get(i);
      Parameter p = new Parameter(paramEl.getAttribute("type"), i, paramEl.getAttribute("name"));
      p.setEncoded(Boolean.valueOf(paramEl.getAttribute("encoded")));
      p.setDefaultValue(paramEl.getAttribute("defaultValue"));
      String pClass = paramEl.getAttribute("class");
      if (!StringUtils.isEmpty(pClass)) {
        try {
          p.setJavaType(ClassLoaderUtils.loadClass(pClass, ResourceUtils.class));
        } catch (Exception ex) {
          throw new RuntimeException(ex);
        }
      }
      params.add(p);
    }
    op.setParameters(params);
    return op;
  }

  public static Object[] createConstructorArguments(
      Constructor<?> c, Message m, boolean perRequest) {
    return createConstructorArguments(c, m, perRequest, null);
  }

  public static Object[] createConstructorArguments(
      Constructor<?> c, Message m, boolean perRequest, Map<Class<?>, Object> contextValues) {
    Class<?>[] params = c.getParameterTypes();
    Annotation[][] anns = c.getParameterAnnotations();
    Type[] genericTypes = c.getGenericParameterTypes();
    @SuppressWarnings("unchecked")
    MultivaluedMap<String, String> templateValues =
        m == null ? null : (MultivaluedMap<String, String>) m.get(URITemplate.TEMPLATE_PARAMETERS);
    Object[] values = new Object[params.length];
    for (int i = 0; i < params.length; i++) {
      if (AnnotationUtils.getAnnotation(anns[i], Context.class) != null) {
        Object contextValue = contextValues != null ? contextValues.get(params[i]) : null;
        if (contextValue == null) {
          if (perRequest) {
            values[i] = JAXRSUtils.createContextValue(m, genericTypes[i], params[i]);
          } else {
            values[i] = InjectionUtils.createThreadLocalProxy(params[i]);
          }
        } else {
          values[i] = contextValue;
        }
      } else {
        // this branch won't execute for singletons given that the found constructor
        // is guaranteed to have only Context parameters, if any, for singletons
        Parameter p = ResourceUtils.getParameter(i, anns[i], params[i]);
        values[i] =
            JAXRSUtils.createHttpParameterValue(
                p, params[i], genericTypes[i], anns[i], m, templateValues, null);
      }
    }
    return values;
  }

  public static JAXRSServerFactoryBean createApplication(Application app, boolean ignoreAppPath) {
    return createApplication(app, ignoreAppPath, false);
  }

  @SuppressWarnings("unchecked")
  public static JAXRSServerFactoryBean createApplication(
      Application app, boolean ignoreAppPath, boolean staticSubresourceResolution) {

    Set<Object> singletons = app.getSingletons();
    verifySingletons(singletons);

    List<Class<?>> resourceClasses = new ArrayList<Class<?>>();
    List<Object> providers = new ArrayList<Object>();
    List<Feature> features = new ArrayList<Feature>();
    Map<Class<?>, ResourceProvider> map = new HashMap<Class<?>, ResourceProvider>();

    // Note, app.getClasses() returns a list of per-request classes
    // or singleton provider classes
    for (Class<?> cls : app.getClasses()) {
      if (isValidApplicationClass(cls, singletons)) {
        if (isValidProvider(cls)) {
          providers.add(createProviderInstance(cls));
        } else if (Feature.class.isAssignableFrom(cls)) {
          features.add(createFeatureInstance((Class<? extends Feature>) cls));
        } else {
          resourceClasses.add(cls);
          map.put(cls, new PerRequestResourceProvider(cls));
        }
      }
    }

    // we can get either a provider or resource class here
    for (Object o : singletons) {
      if (isValidProvider(o.getClass())) {
        providers.add(o);
      } else if (o instanceof Feature) {
        features.add((Feature) o);
      } else {
        resourceClasses.add(o.getClass());
        map.put(o.getClass(), new SingletonResourceProvider(o));
      }
    }

    JAXRSServerFactoryBean bean = new JAXRSServerFactoryBean();
    String address = "/";
    if (!ignoreAppPath) {
      ApplicationPath appPath = app.getClass().getAnnotation(ApplicationPath.class);
      if (appPath != null) {
        address = appPath.value();
      }
    }
    if (!address.startsWith("/")) {
      address = "/" + address;
    }
    bean.setAddress(address);
    bean.setStaticSubresourceResolution(staticSubresourceResolution);
    bean.setResourceClasses(resourceClasses);
    bean.setProviders(providers);
    bean.setFeatures(features);
    for (Map.Entry<Class<?>, ResourceProvider> entry : map.entrySet()) {
      bean.setResourceProvider(entry.getKey(), entry.getValue());
    }
    Map<String, Object> appProps = app.getProperties();
    if (appProps != null) {
      bean.getProperties(true).putAll(appProps);
    }
    bean.setApplication(app);

    return bean;
  }

  public static Object createProviderInstance(Class<?> cls) {
    try {
      Constructor<?> c = ResourceUtils.findResourceConstructor(cls, false);
      if (c.getParameterTypes().length == 0) {
        return c.newInstance();
      } else {
        return c;
      }
    } catch (Throwable ex) {
      throw new RuntimeException("Provider " + cls.getName() + " can not be created", ex);
    }
  }

  public static Feature createFeatureInstance(Class<? extends Feature> cls) {
    try {
      Constructor<?> c = ResourceUtils.findResourceConstructor(cls, false);

      if (c == null) {
        throw new RuntimeException("No valid constructor found for " + cls.getName());
      }

      return (Feature) c.newInstance();
    } catch (Throwable ex) {
      throw new RuntimeException("Feature " + cls.getName() + " can not be created", ex);
    }
  }

  private static boolean isValidProvider(Class<?> c) {
    if (c == null || c == Object.class) {
      return false;
    }
    if (c.getAnnotation(Provider.class) != null) {
      return true;
    }
    for (Class<?> itf : c.getInterfaces()) {
      if (SERVER_PROVIDER_CLASS_NAMES.contains(itf.getName())) {
        return true;
      }
    }
    return isValidProvider(c.getSuperclass());
  }

  private static void verifySingletons(Set<Object> singletons) {
    if (singletons.isEmpty()) {
      return;
    }
    Set<String> map = new HashSet<String>();
    for (Object s : singletons) {
      if (map.contains(s.getClass().getName())) {
        throw new RuntimeException(
            "More than one instance of the same singleton class "
                + s.getClass().getName()
                + " is available");
      } else {
        map.add(s.getClass().getName());
      }
    }
  }

  public static boolean isValidResourceClass(Class<?> c) {
    if (c.isInterface() || Modifier.isAbstract(c.getModifiers())) {
      LOG.info("Ignoring invalid resource class " + c.getName());
      return false;
    }
    return true;
  }

  private static boolean isValidApplicationClass(Class<?> c, Set<Object> singletons) {
    if (!isValidResourceClass(c)) {
      return false;
    }
    for (Object s : singletons) {
      if (c == s.getClass()) {
        LOG.info(
            "Ignoring per-request resource class "
                + c.getName()
                + " as it is also registered as singleton");
        return false;
      }
    }
    return true;
  }

  // TODO : consider moving JAXBDataBinding.createContext to JAXBUtils
  public static JAXBContext createJaxbContext(
      Set<Class<?>> classes, Class<?>[] extraClass, Map<String, Object> contextProperties) {
    if (classes == null || classes.isEmpty()) {
      return null;
    }
    JAXBUtils.scanPackages(classes, extraClass, null);

    JAXBContext ctx;
    try {
      ctx = JAXBContext.newInstance(classes.toArray(new Class[classes.size()]), contextProperties);
      return ctx;
    } catch (JAXBException ex) {
      LOG.log(Level.WARNING, "No JAXB context can be created", ex);
    }
    return null;
  }
}
예제 #2
0
  /**
   * Validate incoming MAPs
   *
   * @param maps the incoming MAPs
   * @param message the current message
   * @return true if incoming MAPs are valid
   * @pre inbound message, not requestor
   */
  private boolean validateIncomingMAPs(AddressingProperties maps, Message message) {
    boolean valid = true;

    if (maps != null) {
      // WSAB spec, section 4.2 validation (SOAPAction must match action
      String sa = SoapActionInInterceptor.getSoapAction(message);
      String s1 = this.getActionUri(message, false);

      if (maps.getAction() == null || maps.getAction().getValue() == null) {
        String reason = BUNDLE.getString("MISSING_ACTION_MESSAGE");

        ContextUtils.storeMAPFaultName(Names.HEADER_REQUIRED_NAME, message);
        ContextUtils.storeMAPFaultReason(reason, message);
        valid = false;
      }

      if (!StringUtils.isEmpty(sa)
          && valid
          && !MessageUtils.isTrue(message.get(MAPAggregator.ACTION_VERIFIED))) {
        if (sa.startsWith("\"")) {
          sa = sa.substring(1, sa.lastIndexOf('"'));
        }
        String action = maps.getAction() == null ? "" : maps.getAction().getValue();
        if (!StringUtils.isEmpty(sa) && !sa.equals(action)) {
          // don't match, must send fault back....
          String reason = BUNDLE.getString("INVALID_ADDRESSING_PROPERTY_MESSAGE");

          ContextUtils.storeMAPFaultName(Names.ACTION_MISMATCH_NAME, message);
          ContextUtils.storeMAPFaultReason(reason, message);
          valid = false;
        } else if (!StringUtils.isEmpty(s1)
            && !action.equals(s1)
            && !action.equals(s1 + "Request")
            && !s1.equals(action + "Request")) {
          // if java first, it's likely to have "Request", if wsdl first,
          // it will depend if the wsdl:input has a name or not. Thus, we'll
          // check both plain and with the "Request" trailer

          // doesn't match what's in the wsdl/annotations
          String reason =
              BundleUtils.getFormattedString(BUNDLE, "ACTION_NOT_SUPPORTED_MSG", action);

          ContextUtils.storeMAPFaultName(Names.ACTION_NOT_SUPPORTED_NAME, message);
          ContextUtils.storeMAPFaultReason(reason, message);
          valid = false;
        }
      }

      AttributedURIType messageID = maps.getMessageID();

      if (!message.getExchange().isOneWay()
          && (messageID == null || messageID.getValue() == null)
          && valid) {
        String reason = BUNDLE.getString("MISSING_ACTION_MESSAGE");

        ContextUtils.storeMAPFaultName(Names.HEADER_REQUIRED_NAME, message);
        ContextUtils.storeMAPFaultReason(reason, message);

        valid = false;
      }

      // Always cache message IDs, even when the message is not valid for some
      // other reason.
      if (!allowDuplicates
          && messageID != null
          && messageID.getValue() != null
          && !messageIdCache.checkUniquenessAndCacheId(messageID.getValue())) {

        LOG.log(Level.WARNING, "DUPLICATE_MESSAGE_ID_MSG", messageID.getValue());

        // Only throw the fault if something else has not already marked the
        // message as invalid.
        if (valid) {
          String reason = BUNDLE.getString("DUPLICATE_MESSAGE_ID_MSG");
          String l7dReason = MessageFormat.format(reason, messageID.getValue());
          ContextUtils.storeMAPFaultName(Names.DUPLICATE_MESSAGE_ID_NAME, message);
          ContextUtils.storeMAPFaultReason(l7dReason, message);
        }

        valid = false;
      }
    } else if (usingAddressingAdvisory) {
      String reason = BUNDLE.getString("MISSING_ACTION_MESSAGE");

      ContextUtils.storeMAPFaultName(Names.HEADER_REQUIRED_NAME, message);
      ContextUtils.storeMAPFaultReason(reason, message);
      valid = false;
    }

    if (Names.INVALID_CARDINALITY_NAME.equals(ContextUtils.retrieveMAPFaultName(message))) {
      valid = false;
    }

    return valid;
  }
예제 #3
0
public class JAXRSOutInterceptor extends AbstractOutDatabindingInterceptor {
  private static final Logger LOG = LogUtils.getL7dLogger(JAXRSOutInterceptor.class);
  private static final ResourceBundle BUNDLE = BundleUtils.getBundle(JAXRSOutInterceptor.class);

  public JAXRSOutInterceptor() {
    super(Phase.MARSHAL);
  }

  public void handleMessage(Message message) {
    ServerProviderFactory providerFactory = ServerProviderFactory.getInstance(message);
    try {
      processResponse(providerFactory, message);
    } finally {
      ServerProviderFactory.releaseRequestState(providerFactory, message);
    }
  }

  private void processResponse(ServerProviderFactory providerFactory, Message message) {

    if (isResponseAlreadyHandled(message)) {
      return;
    }
    MessageContentsList objs = MessageContentsList.getContentsList(message);
    if (objs == null || objs.size() == 0) {
      return;
    }

    Object responseObj = objs.get(0);

    Response response = null;
    if (responseObj instanceof Response) {
      response = (Response) responseObj;
      if (response.getStatus() == 500
          && message.getExchange().get(JAXRSUtils.EXCEPTION_FROM_MAPPER) != null) {
        message.put(Message.RESPONSE_CODE, 500);
        return;
      }
    } else {
      int status = getStatus(message, responseObj != null ? 200 : 204);
      response = JAXRSUtils.toResponseBuilder(status).entity(responseObj).build();
    }

    Exchange exchange = message.getExchange();
    OperationResourceInfo ori =
        (OperationResourceInfo) exchange.get(OperationResourceInfo.class.getName());

    serializeMessage(providerFactory, message, response, ori, true);
  }

  private int getStatus(Message message, int defaultValue) {
    Object customStatus = message.getExchange().get(Message.RESPONSE_CODE);
    return customStatus == null ? defaultValue : (Integer) customStatus;
  }

  private void serializeMessage(
      ServerProviderFactory providerFactory,
      Message message,
      Response theResponse,
      OperationResourceInfo ori,
      boolean firstTry) {

    ResponseImpl response = (ResponseImpl) JAXRSUtils.copyResponseIfNeeded(theResponse);

    final Exchange exchange = message.getExchange();

    boolean headResponse =
        response.getStatus() == 200
            && firstTry
            && ori != null
            && HttpMethod.HEAD.equals(ori.getHttpMethod());
    Object entity = response.getActualEntity();
    if (headResponse && entity != null) {
      LOG.info(new org.apache.cxf.common.i18n.Message("HEAD_WITHOUT_ENTITY", BUNDLE).toString());
      entity = null;
    }

    Method invoked =
        ori == null
            ? null
            : ori.getAnnotatedMethod() != null ? ori.getAnnotatedMethod() : ori.getMethodToInvoke();

    Annotation[] annotations = null;
    Annotation[] staticAnns = ori != null ? ori.getOutAnnotations() : new Annotation[] {};
    Annotation[] responseAnns = response.getEntityAnnotations();
    if (responseAnns != null) {
      annotations = new Annotation[staticAnns.length + responseAnns.length];
      System.arraycopy(staticAnns, 0, annotations, 0, staticAnns.length);
      System.arraycopy(responseAnns, 0, annotations, staticAnns.length, responseAnns.length);
    } else {
      annotations = staticAnns;
    }

    response.setStatus(getActualStatus(response.getStatus(), entity));
    response.setEntity(entity, annotations);

    // Prepare the headers
    MultivaluedMap<String, Object> responseHeaders =
        prepareResponseHeaders(message, response, entity, firstTry);

    // Run the filters
    try {
      JAXRSUtils.runContainerResponseFilters(providerFactory, response, message, ori, invoked);
    } catch (Throwable ex) {
      handleWriteException(providerFactory, message, ex, firstTry);
      return;
    }

    // Write the entity
    entity = InjectionUtils.getEntity(response.getActualEntity());
    setResponseStatus(message, getActualStatus(response.getStatus(), entity));
    if (entity == null) {
      if (!headResponse) {
        responseHeaders.putSingle(HttpHeaders.CONTENT_LENGTH, "0");
        if (MessageUtils.getContextualBoolean(
            message, "remove.content.type.for.empty.response", false)) {
          responseHeaders.remove(HttpHeaders.CONTENT_TYPE);
          message.remove(Message.CONTENT_TYPE);
        }
      }
      HttpUtils.convertHeaderValuesToString(responseHeaders, true);
      return;
    }

    Object ignoreWritersProp = exchange.get(JAXRSUtils.IGNORE_MESSAGE_WRITERS);
    boolean ignoreWriters =
        ignoreWritersProp == null ? false : Boolean.valueOf(ignoreWritersProp.toString());
    if (ignoreWriters) {
      writeResponseToStream(message.getContent(OutputStream.class), entity);
      return;
    }

    MediaType responseMediaType =
        getResponseMediaType(responseHeaders.getFirst(HttpHeaders.CONTENT_TYPE));

    Class<?> serviceCls = invoked != null ? ori.getClassResourceInfo().getServiceClass() : null;
    Class<?> targetType = InjectionUtils.getRawResponseClass(entity);
    Type genericType =
        InjectionUtils.getGenericResponseType(
            invoked, serviceCls, response.getActualEntity(), targetType, exchange);
    targetType = InjectionUtils.updateParamClassToTypeIfNeeded(targetType, genericType);
    annotations = response.getEntityAnnotations();

    List<WriterInterceptor> writers =
        providerFactory.createMessageBodyWriterInterceptor(
            targetType,
            genericType,
            annotations,
            responseMediaType,
            message,
            ori == null ? null : ori.getNameBindings());

    OutputStream outOriginal = message.getContent(OutputStream.class);
    if (writers == null || writers.isEmpty()) {
      writeResponseErrorMessage(
          message, outOriginal, "NO_MSG_WRITER", targetType, responseMediaType);
      return;
    }
    try {
      boolean checkWriters = false;
      if (responseMediaType.isWildcardSubtype()) {
        Produces pM =
            AnnotationUtils.getMethodAnnotation(
                ori == null ? null : ori.getAnnotatedMethod(), Produces.class);
        Produces pC = AnnotationUtils.getClassAnnotation(serviceCls, Produces.class);
        checkWriters = pM == null && pC == null;
      }
      responseMediaType = checkFinalContentType(responseMediaType, writers, checkWriters);
    } catch (Throwable ex) {
      handleWriteException(providerFactory, message, ex, firstTry);
      return;
    }
    String finalResponseContentType = JAXRSUtils.mediaTypeToString(responseMediaType);
    if (LOG.isLoggable(Level.FINE)) {
      LOG.fine("Response content type is: " + finalResponseContentType);
    }
    responseHeaders.putSingle(HttpHeaders.CONTENT_TYPE, finalResponseContentType);
    message.put(Message.CONTENT_TYPE, finalResponseContentType);

    boolean enabled = checkBufferingMode(message, writers, firstTry);
    try {

      try {
        JAXRSUtils.writeMessageBody(
            writers,
            entity,
            targetType,
            genericType,
            annotations,
            responseMediaType,
            responseHeaders,
            message);

        if (isResponseRedirected(message)) {
          return;
        }
        checkCachedStream(message, outOriginal, enabled);
      } finally {
        if (enabled) {
          OutputStream os = message.getContent(OutputStream.class);
          if (os != outOriginal && os instanceof CachedOutputStream) {
            os.close();
          }
          message.setContent(OutputStream.class, outOriginal);
          message.put(XMLStreamWriter.class.getName(), null);
        }
      }

    } catch (Throwable ex) {
      logWriteError(firstTry, targetType, responseMediaType);
      handleWriteException(providerFactory, message, ex, firstTry);
    }
  }

  private MultivaluedMap<String, Object> prepareResponseHeaders(
      Message message, ResponseImpl response, Object entity, boolean firstTry) {
    MultivaluedMap<String, Object> responseHeaders = response.getMetadata();
    @SuppressWarnings("unchecked")
    Map<String, List<Object>> userHeaders =
        (Map<String, List<Object>>) message.get(Message.PROTOCOL_HEADERS);
    if (firstTry && userHeaders != null) {
      responseHeaders.putAll(userHeaders);
    }
    if (entity != null) {
      Object customContentType = responseHeaders.getFirst(HttpHeaders.CONTENT_TYPE);
      if (customContentType == null) {
        String initialResponseContentType = (String) message.get(Message.CONTENT_TYPE);
        if (initialResponseContentType != null) {
          responseHeaders.putSingle(HttpHeaders.CONTENT_TYPE, initialResponseContentType);
        }
      } else {
        message.put(Message.CONTENT_TYPE, customContentType.toString());
      }
    }
    message.put(Message.PROTOCOL_HEADERS, responseHeaders);
    setResponseDate(responseHeaders, firstTry);
    return responseHeaders;
  }

  private MediaType getResponseMediaType(Object mediaTypeHeader) {
    MediaType responseMediaType;
    if (mediaTypeHeader instanceof MediaType) {
      responseMediaType = (MediaType) mediaTypeHeader;
    } else {
      responseMediaType =
          mediaTypeHeader == null
              ? MediaType.WILDCARD_TYPE
              : JAXRSUtils.toMediaType(mediaTypeHeader.toString());
    }
    return responseMediaType;
  }

  private int getActualStatus(int status, Object responseObj) {
    if (status == -1) {
      return responseObj == null ? 204 : 200;
    } else {
      return status;
    }
  }

  private boolean checkBufferingMode(Message m, List<WriterInterceptor> writers, boolean firstTry) {
    if (!firstTry) {
      return false;
    }
    WriterInterceptor last = writers.get(writers.size() - 1);
    MessageBodyWriter<Object> w = ((WriterInterceptorMBW) last).getMBW();
    Object outBuf = m.getContextualProperty(OUT_BUFFERING);
    boolean enabled = MessageUtils.isTrue(outBuf);
    boolean configurableProvider = w instanceof AbstractConfigurableProvider;
    if (!enabled && outBuf == null && configurableProvider) {
      enabled = ((AbstractConfigurableProvider) w).getEnableBuffering();
    }
    if (enabled) {
      boolean streamingOn =
          configurableProvider ? ((AbstractConfigurableProvider) w).getEnableStreaming() : false;
      if (streamingOn) {
        m.setContent(XMLStreamWriter.class, new CachingXmlEventWriter());
      } else {
        m.setContent(OutputStream.class, new CachedOutputStream());
      }
    }
    return enabled;
  }

  private void checkCachedStream(Message m, OutputStream osOriginal, boolean enabled)
      throws Exception {
    XMLStreamWriter writer = null;
    if (enabled) {
      writer = m.getContent(XMLStreamWriter.class);
    } else {
      writer = (XMLStreamWriter) m.get(XMLStreamWriter.class.getName());
    }
    if (writer instanceof CachingXmlEventWriter) {
      CachingXmlEventWriter cache = (CachingXmlEventWriter) writer;
      if (cache.getEvents().size() != 0) {
        XMLStreamWriter origWriter = null;
        try {
          origWriter = StaxUtils.createXMLStreamWriter(osOriginal);
          for (XMLEvent event : cache.getEvents()) {
            StaxUtils.writeEvent(event, origWriter);
          }
        } finally {
          StaxUtils.close(origWriter);
        }
      }
      m.setContent(XMLStreamWriter.class, null);
      return;
    }
    if (enabled) {
      OutputStream os = m.getContent(OutputStream.class);
      if (os != osOriginal && os instanceof CachedOutputStream) {
        CachedOutputStream cos = (CachedOutputStream) os;
        if (cos.size() != 0) {
          cos.writeCacheTo(osOriginal);
        }
      }
    }
  }

  private void logWriteError(boolean firstTry, Class<?> cls, MediaType ct) {
    if (firstTry) {
      JAXRSUtils.logMessageHandlerProblem("MSG_WRITER_PROBLEM", cls, ct);
    }
  }

  private void handleWriteException(
      ServerProviderFactory pf, Message message, Throwable ex, boolean firstTry) {
    Response excResponse = null;
    if (firstTry) {
      excResponse = JAXRSUtils.convertFaultToResponse(ex, message);
    } else {
      message.getExchange().put(JAXRSUtils.SECOND_JAXRS_EXCEPTION, Boolean.TRUE);
    }
    if (excResponse == null) {
      setResponseStatus(message, 500);
      throw new Fault(ex);
    } else {
      serializeMessage(pf, message, excResponse, null, false);
    }
  }

  private void writeResponseErrorMessage(
      Message message, OutputStream out, String name, Class<?> cls, MediaType ct) {
    message.put(Message.CONTENT_TYPE, "text/plain");
    message.put(Message.RESPONSE_CODE, 500);
    try {
      String errorMessage = JAXRSUtils.logMessageHandlerProblem(name, cls, ct);
      if (out != null) {
        out.write(errorMessage.getBytes(StandardCharsets.UTF_8));
      }
    } catch (IOException another) {
      // ignore
    }
  }

  private MediaType checkFinalContentType(
      MediaType mt, List<WriterInterceptor> writers, boolean checkWriters) {
    if (checkWriters) {
      int mbwIndex = writers.size() == 1 ? 0 : writers.size() - 1;
      MessageBodyWriter<Object> writer = ((WriterInterceptorMBW) writers.get(mbwIndex)).getMBW();
      Produces pm = writer.getClass().getAnnotation(Produces.class);
      if (pm != null) {
        List<MediaType> sorted =
            JAXRSUtils.sortMediaTypes(
                JAXRSUtils.getMediaTypes(pm.value()), JAXRSUtils.MEDIA_TYPE_QS_PARAM);
        mt = JAXRSUtils.intersectMimeTypes(sorted, mt).get(0);
      }
    }
    if (mt.isWildcardType() || mt.isWildcardSubtype()) {
      if ("application".equals(mt.getType()) || mt.isWildcardType()) {
        mt = MediaType.APPLICATION_OCTET_STREAM_TYPE;
      } else {
        throw ExceptionUtils.toNotAcceptableException(null, null);
      }
    }
    return mt;
  }

  private void setResponseDate(MultivaluedMap<String, Object> headers, boolean firstTry) {
    if (!firstTry || headers.containsKey(HttpHeaders.DATE)) {
      return;
    }
    SimpleDateFormat format = HttpUtils.getHttpDateFormat();
    headers.putSingle(HttpHeaders.DATE, format.format(new Date()));
  }

  private boolean isResponseAlreadyHandled(Message m) {
    return isResponseAlreadyCommited(m) || isResponseRedirected(m);
  }

  private boolean isResponseAlreadyCommited(Message m) {
    return Boolean.TRUE.equals(m.getExchange().get(AbstractHTTPDestination.RESPONSE_COMMITED));
  }

  private boolean isResponseRedirected(Message m) {
    return Boolean.TRUE.equals(m.getExchange().get(AbstractHTTPDestination.REQUEST_REDIRECTED));
  }

  private void writeResponseToStream(OutputStream os, Object responseObj) {
    try {
      byte[] bytes = responseObj.toString().getBytes(StandardCharsets.UTF_8);
      os.write(bytes, 0, bytes.length);
    } catch (Exception ex) {
      LOG.severe("Problem with writing the data to the output stream");
      ex.printStackTrace();
      throw new RuntimeException(ex);
    }
  }

  private void setResponseStatus(Message message, int status) {
    message.put(Message.RESPONSE_CODE, status);
    boolean responseHeadersCopied = isResponseHeadersCopied(message);
    if (responseHeadersCopied) {
      HttpServletResponse response =
          (HttpServletResponse) message.get(AbstractHTTPDestination.HTTP_RESPONSE);
      response.setStatus(status);
    }
  }

  // Some CXF interceptors such as FIStaxOutInterceptor will indirectly initiate
  // an early copying of response code and headers into the HttpServletResponse
  // TODO : Pushing the filter processing and copying response headers into say
  // PRE-LOGICAl and PREPARE_SEND interceptors will most likely be a good thing
  // however JAX-RS MessageBodyWriters are also allowed to add response headers
  // which is reason why a MultipartMap parameter in MessageBodyWriter.writeTo
  // method is modifiable. Thus we do need to know if the initial copy has already
  // occurred: for now we will just use to ensure the correct status is set
  private boolean isResponseHeadersCopied(Message message) {
    return MessageUtils.isTrue(message.get(AbstractHTTPDestination.RESPONSE_HEADERS_COPIED));
  }

  public void handleFault(Message message) {
    // complete
  }
}
public abstract class AbstractConfigurableProvider {
  protected static final ResourceBundle BUNDLE = BundleUtils.getBundle(AbstractJAXBProvider.class);
  protected static final Logger LOG = LogUtils.getL7dLogger(AbstractJAXBProvider.class);

  private List<String> consumeMediaTypes;
  private List<String> produceMediaTypes;
  private boolean enableBuffering;
  private boolean enableStreaming;
  private Bus bus;

  /**
   * Sets the Bus
   *
   * @param b
   */
  public void setBus(Bus b) {
    if (bus != null) {
      bus = b;
    }
  }

  /**
   * Gets the Bus. Providers may use the bus to resolve resource references. Example:
   * ResourceUtils.getResourceStream(reference, this.getBus())
   *
   * @return
   */
  public Bus getBus() {
    return bus != null ? bus : BusFactory.getThreadDefaultBus();
  }

  /**
   * Sets custom Consumes media types; can be used to override static {@link Consumes} annotation
   * value set on the provider.
   *
   * @param types the media types
   */
  public void setConsumeMediaTypes(List<String> types) {
    consumeMediaTypes = types;
  }

  /**
   * Gets the custom Consumes media types
   *
   * @return media types
   */
  public List<String> getConsumeMediaTypes() {
    return consumeMediaTypes;
  }

  /**
   * Sets custom Produces media types; can be used to override static {@link Produces} annotation
   * value set on the provider.
   *
   * @param types the media types
   */
  public void setProduceMediaTypes(List<String> types) {
    produceMediaTypes = types;
  }

  /**
   * Gets the custom Produces media types
   *
   * @return media types
   */
  public List<String> getProduceMediaTypes() {
    return produceMediaTypes;
  }

  /**
   * Enables the buffering mode. If set to true then the runtime will ensure that the provider
   * writes to a cached stream.
   *
   * <p>For example, the JAXB marshalling process may fail after the initial XML tags have already
   * been written out to the HTTP output stream. Enabling the buffering ensures no incomplete
   * payloads are sent back to clients in case of marshalling errors at the cost of the initial
   * buffering - which might be negligible for small payloads.
   *
   * @param enableBuf the value of the buffering mode, false is default.
   */
  public void setEnableBuffering(boolean enableBuf) {
    enableBuffering = enableBuf;
  }

  /**
   * Gets the value of the buffering mode
   *
   * @return true if the buffering is enabled
   */
  public boolean getEnableBuffering() {
    return enableBuffering;
  }

  /**
   * Enables the support for streaming. XML-aware providers which prefer writing to Stax
   * XMLStreamWriter can set this value to true. Additionally, if the streaming and the buffering
   * modes are enabled, the runtime will ensure the XMLStreamWriter events are cached properly.
   *
   * @param enableStream the value of the streaming mode, false is default.
   */
  public void setEnableStreaming(boolean enableStream) {
    enableStreaming = enableStream;
  }

  /**
   * Gets the value of the streaming mode
   *
   * @return true if the streaming is enabled
   */
  public boolean getEnableStreaming() {
    return enableStreaming;
  }

  /**
   * Gives providers a chance to introspect the JAX-RS model classes. For example, the JAXB provider
   * may use the model classes to create a single composite JAXBContext supporting all the
   * JAXB-annotated root resource classes/types.
   *
   * @param resources
   */
  public void init(List<ClassResourceInfo> resources) {
    // complete
  }

  protected boolean isPayloadEmpty(HttpHeaders headers) {
    if (headers != null) {
      return isPayloadEmpty(headers.getRequestHeaders());
    } else {
      return false;
    }
  }

  protected boolean isPayloadEmpty(MultivaluedMap<String, String> headers) {
    return HttpUtils.isPayloadEmpty(headers);
  }

  protected void reportEmptyContentLength() throws NoContentException {
    String message = new org.apache.cxf.common.i18n.Message("EMPTY_BODY", BUNDLE).toString();
    LOG.warning(message);
    throw new NoContentException(message);
  }
}
@NoJSR250Annotations(unlessNull = "bus")
public final class ConduitInitiatorManagerImpl implements ConduitInitiatorManager {

  private static final ResourceBundle BUNDLE =
      BundleUtils.getBundle(ConduitInitiatorManagerImpl.class);

  Map<String, ConduitInitiator> conduitInitiators;
  Set<String> failed = new CopyOnWriteArraySet<String>();
  Set<String> loaded = new CopyOnWriteArraySet<String>();

  private Bus bus;

  public ConduitInitiatorManagerImpl() {
    conduitInitiators = new ConcurrentHashMap<String, ConduitInitiator>(8, 0.75f, 4);
  }

  public ConduitInitiatorManagerImpl(Bus b) {
    conduitInitiators = new ConcurrentHashMap<String, ConduitInitiator>(8, 0.75f, 4);
    setBus(b);
  }

  @Resource
  public void setBus(Bus b) {
    bus = b;
    if (null != bus) {
      bus.setExtension(this, ConduitInitiatorManager.class);
    }
  }

  /*
   * (non-Javadoc)
   *
   * @see org.apache.cxf.bus.ConduitInitiatorManager#registerConduitInitiator(java.lang.String,
   *      org.apache.cxf.transports.ConduitInitiator)
   */
  public void registerConduitInitiator(String namespace, ConduitInitiator factory) {
    conduitInitiators.put(namespace, factory);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.apache.cxf.bus.ConduitInitiatorManager#deregisterConduitInitiator(java.lang.String)
   */
  public void deregisterConduitInitiator(String namespace) {
    conduitInitiators.remove(namespace);
  }

  /*
   * (non-Javadoc)
   *
   * @see org.apache.cxf.bus.ConduitInitiatorManager#ConduitInitiator(java.lang.String)
   */
  /**
   * Returns the conduit initiator for the given namespace, constructing it (and storing in the
   * cache for future reference) if necessary, using its list of factory classname to namespace
   * mappings.
   *
   * @param namespace the namespace.
   */
  public ConduitInitiator getConduitInitiator(String namespace) throws BusException {
    ConduitInitiator factory = conduitInitiators.get(namespace);
    if (factory == null && !failed.contains(namespace)) {
      factory =
          new TransportFinder<ConduitInitiator>(
                  bus, conduitInitiators, loaded, ConduitInitiator.class)
              .findTransportForNamespace(namespace);
    }
    if (factory == null) {
      failed.add(namespace);
      throw new BusException(new Message("NO_CONDUIT_INITIATOR", BUNDLE, namespace));
    }
    return factory;
  }

  @PreDestroy
  public void shutdown() {
    // nothing to do
  }

  public ConduitInitiator getConduitInitiatorForUri(String uri) {
    ConduitInitiator factory =
        new TransportFinder<ConduitInitiator>(
                bus, conduitInitiators, loaded, ConduitInitiator.class)
            .findTransportForURI(uri);

    return factory;
  }
}
예제 #6
0
public class ColocOutInterceptor extends AbstractPhaseInterceptor<Message> {
  private static final ResourceBundle BUNDLE = BundleUtils.getBundle(ColocOutInterceptor.class);
  private static final Logger LOG = LogUtils.getL7dLogger(ClientImpl.class);
  private static final String COLOCATED = Message.class.getName() + ".COLOCATED";
  private MessageObserver colocObserver;
  private Bus bus;

  public ColocOutInterceptor() {
    super(Phase.POST_LOGICAL);
  }

  public ColocOutInterceptor(Bus b) {
    super(Phase.POST_LOGICAL);
    bus = b;
  }

  public void setBus(Bus bus) {
    this.bus = bus;
  }

  public void handleMessage(Message message) throws Fault {
    if (bus == null) {
      bus = message.getExchange().getBus();
      if (bus == null) {
        bus = BusFactory.getDefaultBus(false);
      }
      if (bus == null) {
        throw new Fault(new org.apache.cxf.common.i18n.Message("BUS_NOT_FOUND", BUNDLE));
      }
    }

    ServerRegistry registry = bus.getExtension(ServerRegistry.class);

    if (registry == null) {
      throw new Fault(new org.apache.cxf.common.i18n.Message("SERVER_REGISTRY_NOT_FOUND", BUNDLE));
    }

    Exchange exchange = message.getExchange();
    Endpoint senderEndpoint = exchange.getEndpoint();

    if (senderEndpoint == null) {
      throw new Fault(new org.apache.cxf.common.i18n.Message("ENDPOINT_NOT_FOUND", BUNDLE));
    }

    BindingOperationInfo boi = exchange.getBindingOperationInfo();

    if (boi == null) {
      throw new Fault(new org.apache.cxf.common.i18n.Message("OPERATIONINFO_NOT_FOUND", BUNDLE));
    }

    Server srv = isColocated(registry.getServers(), senderEndpoint, boi);

    if (srv != null) {
      if (LOG.isLoggable(Level.FINE)) {
        LOG.fine("Operation:" + boi.getName() + " dispatched as colocated call.");
      }

      InterceptorChain outChain = message.getInterceptorChain();
      outChain.abort();
      exchange.put(Bus.class, bus);
      message.put(COLOCATED, Boolean.TRUE);
      message.put(Message.WSDL_OPERATION, boi.getName());
      message.put(Message.WSDL_INTERFACE, boi.getBinding().getInterface().getName());
      invokeColocObserver(message, srv.getEndpoint());
      if (!exchange.isOneWay()) {
        invokeInboundChain(exchange, senderEndpoint);
      }
    } else {
      if (LOG.isLoggable(Level.FINE)) {
        LOG.fine("Operation:" + boi.getName() + " dispatched as remote call.");
      }

      message.put(COLOCATED, Boolean.FALSE);
    }
  }

  protected void invokeColocObserver(Message outMsg, Endpoint inboundEndpoint) {
    if (colocObserver == null) {
      colocObserver = new ColocMessageObserver(inboundEndpoint, bus);
    }
    if (LOG.isLoggable(Level.FINE)) {
      LOG.fine("Invoke on Coloc Observer.");
    }

    colocObserver.onMessage(outMsg);
  }

  protected void invokeInboundChain(Exchange ex, Endpoint ep) {
    Message m = getInBoundMessage(ex);
    Message inMsg = ep.getBinding().createMessage();
    MessageImpl.copyContent(m, inMsg);

    // Copy Response Context to Client inBound Message
    // TODO a Context Filter Strategy required.
    inMsg.putAll(m);

    inMsg.put(Message.REQUESTOR_ROLE, Boolean.TRUE);
    inMsg.put(Message.INBOUND_MESSAGE, Boolean.TRUE);
    inMsg.setExchange(ex);

    Exception exc = inMsg.getContent(Exception.class);
    if (exc != null) {
      ex.setInFaultMessage(inMsg);
      ColocInFaultObserver observer = new ColocInFaultObserver(bus);
      observer.onMessage(inMsg);
    } else {
      // Handle Response
      ex.setInMessage(inMsg);
      PhaseManager pm = bus.getExtension(PhaseManager.class);
      SortedSet<Phase> phases = new TreeSet<Phase>(pm.getInPhases());
      ColocUtil.setPhases(phases, Phase.USER_LOGICAL, Phase.PRE_INVOKE);

      InterceptorChain chain = ColocUtil.getInInterceptorChain(ex, phases);
      inMsg.setInterceptorChain(chain);
      chain.doIntercept(inMsg);
    }
    ex.put(ClientImpl.FINISHED, Boolean.TRUE);
  }

  protected Message getInBoundMessage(Exchange ex) {
    return (ex.getInFaultMessage() != null) ? ex.getInFaultMessage() : ex.getInMessage();
  }

  protected void setMessageObserver(MessageObserver observer) {
    colocObserver = observer;
  }

  protected Server isColocated(List<Server> servers, Endpoint endpoint, BindingOperationInfo boi) {
    if (servers != null) {
      Service senderService = endpoint.getService();
      EndpointInfo senderEI = endpoint.getEndpointInfo();
      for (Server s : servers) {
        Endpoint receiverEndpoint = s.getEndpoint();
        Service receiverService = receiverEndpoint.getService();
        EndpointInfo receiverEI = receiverEndpoint.getEndpointInfo();
        if (receiverService.getName().equals(senderService.getName())
            && receiverEI.getName().equals(senderEI.getName())) {
          // Check For Operation Match.
          BindingOperationInfo receiverOI = receiverEI.getBinding().getOperation(boi.getName());
          if (receiverOI != null && isCompatibleOperationInfo(boi, receiverOI)) {
            return s;
          }
        }
      }
    }

    return null;
  }

  protected boolean isSameOperationInfo(
      BindingOperationInfo sender, BindingOperationInfo receiver) {
    return ColocUtil.isSameOperationInfo(sender.getOperationInfo(), receiver.getOperationInfo());
  }

  protected boolean isCompatibleOperationInfo(
      BindingOperationInfo sender, BindingOperationInfo receiver) {
    return ColocUtil.isCompatibleOperationInfo(
        sender.getOperationInfo(), receiver.getOperationInfo());
  }

  public void setExchangeProperties(Exchange exchange, Endpoint ep) {
    exchange.put(Endpoint.class, ep);
    exchange.put(Service.class, ep.getService());
    exchange.put(Binding.class, ep.getBinding());
    exchange.put(Bus.class, bus == null ? BusFactory.getDefaultBus(false) : bus);
  }
}
예제 #7
0
/** Proxy-based client implementation */
public class ClientProxyImpl extends AbstractClient
    implements InvocationHandlerAware, InvocationHandler {

  private static final Logger LOG = LogUtils.getL7dLogger(ClientProxyImpl.class);
  private static final ResourceBundle BUNDLE = BundleUtils.getBundle(ClientProxyImpl.class);
  private static final String SLASH = "/";
  private static final String BUFFER_PROXY_RESPONSE = "buffer.proxy.response";

  private ClassResourceInfo cri;
  private ClassLoader proxyLoader;
  private boolean inheritHeaders;
  private boolean isRoot;
  private Map<String, Object> valuesMap = Collections.emptyMap();
  private BodyWriter bodyWriter = new BodyWriter();

  public ClientProxyImpl(
      URI baseURI,
      ClassLoader loader,
      ClassResourceInfo cri,
      boolean isRoot,
      boolean inheritHeaders,
      Object... varValues) {
    this(new LocalClientState(baseURI), loader, cri, isRoot, inheritHeaders, varValues);
  }

  public ClientProxyImpl(
      ClientState initialState,
      ClassLoader loader,
      ClassResourceInfo cri,
      boolean isRoot,
      boolean inheritHeaders,
      Object... varValues) {
    super(initialState);
    this.proxyLoader = loader;
    this.cri = cri;
    this.isRoot = isRoot;
    this.inheritHeaders = inheritHeaders;
    initValuesMap(varValues);
  }

  private void initValuesMap(Object... varValues) {
    if (isRoot) {
      List<String> vars = cri.getURITemplate().getVariables();
      valuesMap = new LinkedHashMap<String, Object>();
      for (int i = 0; i < vars.size(); i++) {
        if (varValues.length > 0) {
          if (i < varValues.length) {
            valuesMap.put(vars.get(i), varValues[i]);
          } else {
            org.apache.cxf.common.i18n.Message msg =
                new org.apache.cxf.common.i18n.Message(
                    "ROOT_VARS_MISMATCH", BUNDLE, vars.size(), varValues.length);
            LOG.info(msg.toString());
            break;
          }
        } else {
          valuesMap.put(vars.get(i), "");
        }
      }
    }
  }

  /**
   * Updates the current state if Client method is invoked, otherwise does the remote invocation or
   * returns a new proxy if subresource method is invoked. Can throw an expected exception if
   * ResponseExceptionMapper is registered
   */
  public Object invoke(Object o, Method m, Object[] params) throws Throwable {

    Class<?> declaringClass = m.getDeclaringClass();
    if (Client.class == declaringClass
        || InvocationHandlerAware.class == declaringClass
        || Object.class == declaringClass) {
      return m.invoke(this, params);
    }
    resetResponse();
    OperationResourceInfo ori = cri.getMethodDispatcher().getOperationResourceInfo(m);
    if (ori == null) {
      reportInvalidResourceMethod(m, "INVALID_RESOURCE_METHOD");
    }

    MultivaluedMap<ParameterType, Parameter> types = getParametersInfo(m, params, ori);
    List<Parameter> beanParamsList = getParameters(types, ParameterType.BEAN);

    int bodyIndex = getBodyIndex(types, ori);

    List<Object> pathParams = getPathParamValues(m, params, types, beanParamsList, ori, bodyIndex);

    UriBuilder builder = getCurrentBuilder().clone();
    if (isRoot) {
      addNonEmptyPath(builder, ori.getClassResourceInfo().getURITemplate().getValue());
    }
    addNonEmptyPath(builder, ori.getURITemplate().getValue());

    handleMatrixes(m, params, types, beanParamsList, builder);
    handleQueries(m, params, types, beanParamsList, builder);

    URI uri = builder.buildFromEncoded(pathParams.toArray()).normalize();

    MultivaluedMap<String, String> headers = getHeaders();
    MultivaluedMap<String, String> paramHeaders = new MetadataMap<String, String>();
    handleHeaders(m, params, paramHeaders, beanParamsList, types);
    handleCookies(m, params, paramHeaders, beanParamsList, types);

    if (ori.isSubResourceLocator()) {
      ClassResourceInfo subCri = cri.getSubResource(m.getReturnType(), m.getReturnType());
      if (subCri == null) {
        reportInvalidResourceMethod(m, "INVALID_SUBRESOURCE");
      }

      MultivaluedMap<String, String> subHeaders = paramHeaders;
      if (inheritHeaders) {
        subHeaders.putAll(headers);
      }

      ClientState newState =
          getState()
              .newState(
                  uri, subHeaders, getTemplateParametersMap(ori.getURITemplate(), pathParams));
      ClientProxyImpl proxyImpl =
          new ClientProxyImpl(newState, proxyLoader, subCri, false, inheritHeaders);
      proxyImpl.setConfiguration(getConfiguration());
      return JAXRSClientFactory.createProxy(m.getReturnType(), proxyLoader, proxyImpl);
    }
    headers.putAll(paramHeaders);

    getState().setTemplates(getTemplateParametersMap(ori.getURITemplate(), pathParams));

    Object body = null;
    if (bodyIndex != -1) {
      body = params[bodyIndex];
      if (body == null) {
        bodyIndex = -1;
      }
    } else if (types.containsKey(ParameterType.FORM)) {
      body = handleForm(m, params, types, beanParamsList);
    } else if (types.containsKey(ParameterType.REQUEST_BODY)) {
      body = handleMultipart(types, ori, params);
    }

    setRequestHeaders(
        headers,
        ori,
        types.containsKey(ParameterType.FORM),
        body == null ? null : body.getClass(),
        m.getReturnType());

    return doChainedInvocation(uri, headers, ori, body, bodyIndex, null, null);
  }

  private void addNonEmptyPath(UriBuilder builder, String pathValue) {
    if (!SLASH.equals(pathValue)) {
      builder.path(pathValue);
    }
  }

  private static MultivaluedMap<ParameterType, Parameter> getParametersInfo(
      Method m, Object[] params, OperationResourceInfo ori) {
    MultivaluedMap<ParameterType, Parameter> map = new MetadataMap<ParameterType, Parameter>();

    List<Parameter> parameters = ori.getParameters();
    if (parameters.size() == 0) {
      return map;
    }
    int requestBodyParam = 0;
    int multipartParam = 0;
    for (Parameter p : parameters) {
      if (isIgnorableParameter(m, p)) {
        continue;
      }
      if (p.getType() == ParameterType.REQUEST_BODY) {
        requestBodyParam++;
        if (getMultipart(ori, p.getIndex()) != null) {
          multipartParam++;
        }
      }
      map.add(p.getType(), p);
    }

    if (map.containsKey(ParameterType.REQUEST_BODY)) {
      if (requestBodyParam > 1 && requestBodyParam != multipartParam) {
        reportInvalidResourceMethod(ori.getMethodToInvoke(), "SINGLE_BODY_ONLY");
      }
      if (map.containsKey(ParameterType.FORM)) {
        reportInvalidResourceMethod(ori.getMethodToInvoke(), "ONLY_FORM_ALLOWED");
      }
    }
    return map;
  }

  private static boolean isIgnorableParameter(Method m, Parameter p) {
    if (p.getType() == ParameterType.CONTEXT) {
      return true;
    }
    return p.getType() == ParameterType.REQUEST_BODY
        && m.getParameterTypes()[p.getIndex()] == AsyncResponse.class;
  }

  private static int getBodyIndex(
      MultivaluedMap<ParameterType, Parameter> map, OperationResourceInfo ori) {
    List<Parameter> list = map.get(ParameterType.REQUEST_BODY);
    int index = list == null || list.size() > 1 ? -1 : list.get(0).getIndex();
    if (ori.isSubResourceLocator() && index != -1) {
      reportInvalidResourceMethod(ori.getMethodToInvoke(), "NO_BODY_IN_SUBRESOURCE");
    }
    return index;
  }

  private void checkResponse(Method m, Response r, Message inMessage) throws Throwable {
    Throwable t = null;
    int status = r.getStatus();

    if (status >= 300) {
      Class<?>[] exTypes = m.getExceptionTypes();
      if (exTypes.length == 0) {
        exTypes = new Class[] {WebApplicationException.class};
      }
      for (Class<?> exType : exTypes) {
        ResponseExceptionMapper<?> mapper = findExceptionMapper(inMessage, exType);
        if (mapper != null) {
          t = mapper.fromResponse(r);
          if (t != null) {
            throw t;
          }
        }
      }

      if ((t == null)
          && (m.getReturnType() == Response.class)
          && (m.getExceptionTypes().length == 0)) {
        return;
      }

      t = convertToWebApplicationException(r);

      if (inMessage.getExchange().get(Message.RESPONSE_CODE) == null) {
        throw t;
      }

      Endpoint ep = inMessage.getExchange().getEndpoint();
      inMessage.getExchange().put(InterceptorProvider.class, getConfiguration());
      inMessage.setContent(Exception.class, new Fault(t));
      inMessage.getInterceptorChain().abort();
      if (ep.getInFaultObserver() != null) {
        ep.getInFaultObserver().onMessage(inMessage);
      }

      throw t;
    }
  }

  private static ResponseExceptionMapper<?> findExceptionMapper(Message message, Class<?> exType) {
    ClientProviderFactory pf = ClientProviderFactory.getInstance(message);
    return pf.createResponseExceptionMapper(message, exType);
  }

  private MultivaluedMap<String, String> setRequestHeaders(
      MultivaluedMap<String, String> headers,
      OperationResourceInfo ori,
      boolean formParams,
      Class<?> bodyClass,
      Class<?> responseClass) {
    if (headers.getFirst(HttpHeaders.CONTENT_TYPE) == null) {
      if (formParams || bodyClass != null && MultivaluedMap.class.isAssignableFrom(bodyClass)) {
        headers.putSingle(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED);
      } else {
        String ctType = null;
        List<MediaType> consumeTypes = ori.getConsumeTypes();
        if (!consumeTypes.isEmpty() && !consumeTypes.get(0).equals(MediaType.WILDCARD_TYPE)) {
          ctType = JAXRSUtils.mediaTypeToString(ori.getConsumeTypes().get(0));
        } else if (bodyClass != null) {
          ctType = MediaType.APPLICATION_XML;
        }
        if (ctType != null) {
          headers.putSingle(HttpHeaders.CONTENT_TYPE, ctType);
        }
      }
    }

    List<MediaType> accepts = getAccept(headers);
    if (accepts == null) {
      boolean produceWildcard =
          ori.getProduceTypes().size() == 0
              || ori.getProduceTypes().get(0).equals(MediaType.WILDCARD_TYPE);
      if (produceWildcard) {
        accepts =
            InjectionUtils.isPrimitive(responseClass)
                ? Collections.singletonList(MediaType.TEXT_PLAIN_TYPE)
                : Collections.singletonList(MediaType.APPLICATION_XML_TYPE);
      } else if (responseClass == Void.class || responseClass == Void.TYPE) {
        accepts = Collections.singletonList(MediaType.WILDCARD_TYPE);
      } else {
        accepts = ori.getProduceTypes();
      }

      for (MediaType mt : accepts) {
        headers.add(HttpHeaders.ACCEPT, JAXRSUtils.mediaTypeToString(mt));
      }
    }

    return headers;
  }

  private List<MediaType> getAccept(MultivaluedMap<String, String> allHeaders) {
    List<String> headers = allHeaders.get(HttpHeaders.ACCEPT);
    if (headers == null || headers.size() == 0) {
      return null;
    }
    List<MediaType> types = new ArrayList<MediaType>();
    for (String s : headers) {
      types.add(JAXRSUtils.toMediaType(s));
    }
    return types;
  }

  private List<Object> getPathParamValues(
      Method m,
      Object[] params,
      MultivaluedMap<ParameterType, Parameter> map,
      List<Parameter> beanParams,
      OperationResourceInfo ori,
      int bodyIndex) {
    List<Object> list = new LinkedList<Object>();

    List<String> methodVars = ori.getURITemplate().getVariables();
    List<Parameter> paramsList = getParameters(map, ParameterType.PATH);
    Map<String, BeanPair> beanParamValues = new HashMap<String, BeanPair>(beanParams.size());
    for (Parameter p : beanParams) {
      beanParamValues.putAll(getValuesFromBeanParam(params[p.getIndex()], PathParam.class));
    }
    if (!beanParamValues.isEmpty() && !methodVars.containsAll(beanParamValues.keySet())) {
      List<String> classVars = ori.getClassResourceInfo().getURITemplate().getVariables();
      for (String classVar : classVars) {
        BeanPair pair = beanParamValues.get(classVar);
        if (pair != null) {
          Object paramValue = convertParamValue(pair.getValue(), pair.getAnns());
          if (isRoot) {
            valuesMap.put(classVar, paramValue);
          } else {
            list.add(paramValue);
          }
        }
      }
    }
    if (isRoot) {
      list.addAll(valuesMap.values());
    }

    Map<String, Parameter> paramsMap = new LinkedHashMap<String, Parameter>();
    for (Parameter p : paramsList) {
      if (p.getName().length() == 0) {
        MultivaluedMap<String, Object> values =
            InjectionUtils.extractValuesFromBean(params[p.getIndex()], "");
        for (String var : methodVars) {
          list.addAll(values.get(var));
        }
      } else {
        paramsMap.put(p.getName(), p);
      }
    }

    Object requestBody = bodyIndex == -1 ? null : params[bodyIndex];
    for (String varName : methodVars) {
      Parameter p = paramsMap.remove(varName);
      if (p != null) {
        list.add(convertParamValue(params[p.getIndex()], getParamAnnotations(m, p)));
      } else if (beanParamValues.containsKey(varName)) {
        BeanPair pair = beanParamValues.get(varName);
        list.add(convertParamValue(pair.getValue(), pair.getAnns()));
      } else if (requestBody != null) {
        try {
          Method getter =
              requestBody
                  .getClass()
                  .getMethod("get" + StringUtils.capitalize(varName), new Class<?>[] {});
          list.add(getter.invoke(requestBody, new Object[] {}));
        } catch (Exception ex) {
          // continue
        }
      }
    }

    for (Parameter p : paramsMap.values()) {
      if (valuesMap.containsKey(p.getName())) {
        int index = 0;
        for (Iterator<String> it = valuesMap.keySet().iterator(); it.hasNext(); index++) {
          if (it.next().equals(p.getName()) && index < list.size()) {
            list.remove(index);
            list.add(index, convertParamValue(params[p.getIndex()], null));
            break;
          }
        }
      }
    }

    return list;
  }

  private static Annotation[] getParamAnnotations(Method m, Parameter p) {
    return m.getParameterAnnotations()[p.getIndex()];
  }

  @SuppressWarnings("unchecked")
  private static List<Parameter> getParameters(
      MultivaluedMap<ParameterType, Parameter> map, ParameterType key) {
    return map.get(key) == null ? Collections.EMPTY_LIST : map.get(key);
  }

  private void handleQueries(
      Method m,
      Object[] params,
      MultivaluedMap<ParameterType, Parameter> map,
      List<Parameter> beanParams,
      UriBuilder ub) {
    List<Parameter> qs = getParameters(map, ParameterType.QUERY);
    for (Parameter p : qs) {
      if (params[p.getIndex()] != null) {
        addMatrixQueryParamsToBuilder(
            ub, p.getName(), ParameterType.QUERY, getParamAnnotations(m, p), params[p.getIndex()]);
      }
    }
    for (Parameter p : beanParams) {
      Map<String, BeanPair> values = getValuesFromBeanParam(params[p.getIndex()], QueryParam.class);
      for (Map.Entry<String, BeanPair> entry : values.entrySet()) {
        if (entry.getValue() != null) {
          addMatrixQueryParamsToBuilder(
              ub,
              entry.getKey(),
              ParameterType.QUERY,
              entry.getValue().getAnns(),
              entry.getValue().getValue());
        }
      }
    }
  }

  private Map<String, BeanPair> getValuesFromBeanParam(
      Object bean, Class<? extends Annotation> annClass) {
    Map<String, BeanPair> values = new HashMap<String, BeanPair>();
    getValuesFromBeanParam(bean, annClass, values);
    return values;
  }

  private Map<String, BeanPair> getValuesFromBeanParam(
      Object bean, Class<? extends Annotation> annClass, Map<String, BeanPair> values) {
    for (Method m : bean.getClass().getMethods()) {
      if (m.getName().startsWith("set")) {
        try {
          String propertyName = m.getName().substring(3);
          Annotation annotation = m.getAnnotation(annClass);
          boolean beanParam = m.getAnnotation(BeanParam.class) != null;
          if (annotation != null || beanParam) {
            Method getter = bean.getClass().getMethod("get" + propertyName, new Class[] {});
            Object value = getter.invoke(bean, new Object[] {});
            if (value != null) {
              if (annotation != null) {
                String annotationValue = AnnotationUtils.getAnnotationValue(annotation);
                values.put(annotationValue, new BeanPair(value, m.getParameterAnnotations()[0]));
              } else {
                getValuesFromBeanParam(value, annClass, values);
              }
            }
          } else {
            String fieldName = StringUtils.uncapitalize(propertyName);
            Field f = InjectionUtils.getDeclaredField(bean.getClass(), fieldName);
            if (f == null) {
              continue;
            }
            annotation = f.getAnnotation(annClass);
            if (annotation != null) {
              Object value = ReflectionUtil.accessDeclaredField(f, bean, Object.class);
              if (value != null) {
                String annotationValue = AnnotationUtils.getAnnotationValue(annotation);
                values.put(annotationValue, new BeanPair(value, f.getAnnotations()));
              }
            } else if (f.getAnnotation(BeanParam.class) != null) {
              Object value = ReflectionUtil.accessDeclaredField(f, bean, Object.class);
              if (value != null) {
                getValuesFromBeanParam(value, annClass, values);
              }
            }
          }
        } catch (Throwable t) {
          // ignore
        }
      }
    }
    return values;
  }

  private void handleMatrixes(
      Method m,
      Object[] params,
      MultivaluedMap<ParameterType, Parameter> map,
      List<Parameter> beanParams,
      UriBuilder ub) {
    List<Parameter> mx = getParameters(map, ParameterType.MATRIX);
    for (Parameter p : mx) {
      if (params[p.getIndex()] != null) {
        addMatrixQueryParamsToBuilder(
            ub, p.getName(), ParameterType.MATRIX, getParamAnnotations(m, p), params[p.getIndex()]);
      }
    }
    for (Parameter p : beanParams) {
      Map<String, BeanPair> values =
          getValuesFromBeanParam(params[p.getIndex()], MatrixParam.class);
      for (Map.Entry<String, BeanPair> entry : values.entrySet()) {
        if (entry.getValue() != null) {
          addMatrixQueryParamsToBuilder(
              ub,
              entry.getKey(),
              ParameterType.MATRIX,
              entry.getValue().getAnns(),
              entry.getValue().getValue());
        }
      }
    }
  }

  private MultivaluedMap<String, String> handleForm(
      Method m,
      Object[] params,
      MultivaluedMap<ParameterType, Parameter> map,
      List<Parameter> beanParams) {

    MultivaluedMap<String, String> form = new MetadataMap<String, String>();

    List<Parameter> fm = getParameters(map, ParameterType.FORM);
    for (Parameter p : fm) {
      addFormValue(form, p.getName(), params[p.getIndex()], getParamAnnotations(m, p));
    }
    for (Parameter p : beanParams) {
      Map<String, BeanPair> values = getValuesFromBeanParam(params[p.getIndex()], FormParam.class);
      for (Map.Entry<String, BeanPair> entry : values.entrySet()) {
        addFormValue(form, entry.getKey(), entry.getValue().getValue(), entry.getValue().getAnns());
      }
    }

    return form;
  }

  private void addFormValue(
      MultivaluedMap<String, String> form, String name, Object pValue, Annotation[] anns) {
    if (pValue != null) {
      if (InjectionUtils.isSupportedCollectionOrArray(pValue.getClass())) {
        Collection<?> c =
            pValue.getClass().isArray() ? Arrays.asList((Object[]) pValue) : (Collection<?>) pValue;
        for (Iterator<?> it = c.iterator(); it.hasNext(); ) {
          FormUtils.addPropertyToForm(form, name, convertParamValue(it.next(), anns));
        }
      } else {
        FormUtils.addPropertyToForm(
            form, name, name.isEmpty() ? pValue : convertParamValue(pValue, anns));
      }
    }
  }

  private List<Attachment> handleMultipart(
      MultivaluedMap<ParameterType, Parameter> map, OperationResourceInfo ori, Object[] params) {

    List<Attachment> atts = new LinkedList<Attachment>();
    List<Parameter> fm = getParameters(map, ParameterType.REQUEST_BODY);
    for (Parameter p : fm) {
      Multipart part = getMultipart(ori, p.getIndex());
      if (part != null) {
        Object partObject = params[p.getIndex()];
        if (partObject != null) {
          atts.add(new Attachment(part.value(), part.type(), partObject));
        }
      }
    }
    return atts;
  }

  private void handleHeaders(
      Method m,
      Object[] params,
      MultivaluedMap<String, String> headers,
      List<Parameter> beanParams,
      MultivaluedMap<ParameterType, Parameter> map) {
    List<Parameter> hs = getParameters(map, ParameterType.HEADER);
    for (Parameter p : hs) {
      if (params[p.getIndex()] != null) {
        headers.add(
            p.getName(), convertParamValue(params[p.getIndex()], getParamAnnotations(m, p)));
      }
    }
    for (Parameter p : beanParams) {
      Map<String, BeanPair> values =
          getValuesFromBeanParam(params[p.getIndex()], HeaderParam.class);
      for (Map.Entry<String, BeanPair> entry : values.entrySet()) {
        if (entry.getValue() != null) {
          headers.add(
              entry.getKey(),
              convertParamValue(entry.getValue().getValue(), entry.getValue().getAnns()));
        }
      }
    }
  }

  private static Multipart getMultipart(OperationResourceInfo ori, int index) {
    Method aMethod = ori.getAnnotatedMethod();
    return aMethod != null
        ? AnnotationUtils.getAnnotation(aMethod.getParameterAnnotations()[index], Multipart.class)
        : null;
  }

  private void handleCookies(
      Method m,
      Object[] params,
      MultivaluedMap<String, String> headers,
      List<Parameter> beanParams,
      MultivaluedMap<ParameterType, Parameter> map) {
    List<Parameter> cs = getParameters(map, ParameterType.COOKIE);
    for (Parameter p : cs) {
      if (params[p.getIndex()] != null) {
        headers.add(
            HttpHeaders.COOKIE,
            p.getName()
                + '='
                + convertParamValue(params[p.getIndex()].toString(), getParamAnnotations(m, p)));
      }
    }
    for (Parameter p : beanParams) {
      Map<String, BeanPair> values =
          getValuesFromBeanParam(params[p.getIndex()], CookieParam.class);
      for (Map.Entry<String, BeanPair> entry : values.entrySet()) {
        if (entry.getValue() != null) {
          headers.add(
              HttpHeaders.COOKIE,
              entry.getKey()
                  + "="
                  + convertParamValue(entry.getValue().getValue(), entry.getValue().getAnns()));
        }
      }
    }
  }

  private Object doChainedInvocation(
      URI uri,
      MultivaluedMap<String, String> headers,
      OperationResourceInfo ori,
      Object body,
      int bodyIndex,
      Exchange exchange,
      Map<String, Object> invocationContext)
      throws Throwable {
    Bus configuredBus = getConfiguration().getBus();
    Bus origBus = BusFactory.getAndSetThreadDefaultBus(configuredBus);
    ClassLoaderHolder origLoader = null;
    try {
      ClassLoader loader = configuredBus.getExtension(ClassLoader.class);
      if (loader != null) {
        origLoader = ClassLoaderUtils.setThreadContextClassloader(loader);
      }
      Message outMessage =
          createMessage(body, ori.getHttpMethod(), headers, uri, exchange, invocationContext, true);
      if (bodyIndex != -1) {
        outMessage.put(Type.class, ori.getMethodToInvoke().getGenericParameterTypes()[bodyIndex]);
      }
      outMessage.getExchange().setOneWay(ori.isOneway());
      setSupportOnewayResponseProperty(outMessage);
      outMessage.setContent(OperationResourceInfo.class, ori);
      setPlainOperationNameProperty(outMessage, ori.getMethodToInvoke().getName());
      outMessage.getExchange().put(Method.class, ori.getMethodToInvoke());

      outMessage.put(
          Annotation.class.getName(), getMethodAnnotations(ori.getAnnotatedMethod(), bodyIndex));

      if (body != null) {
        outMessage.put("BODY_INDEX", bodyIndex);
      }
      outMessage.getInterceptorChain().add(bodyWriter);

      Map<String, Object> reqContext = getRequestContext(outMessage);
      reqContext.put(OperationResourceInfo.class.getName(), ori);
      reqContext.put("BODY_INDEX", bodyIndex);

      // execute chain
      doRunInterceptorChain(outMessage);

      Object[] results = preProcessResult(outMessage);
      if (results != null && results.length == 1) {
        return results[0];
      }

      try {
        return handleResponse(outMessage, ori.getClassResourceInfo().getServiceClass());
      } finally {
        completeExchange(outMessage.getExchange(), true);
      }
    } finally {
      if (origLoader != null) {
        origLoader.reset();
      }
      if (origBus != configuredBus) {
        BusFactory.setThreadDefaultBus(origBus);
      }
    }
  }

  @Override
  protected Object retryInvoke(
      URI newRequestURI,
      MultivaluedMap<String, String> headers,
      Object body,
      Exchange exchange,
      Map<String, Object> invContext)
      throws Throwable {

    Map<String, Object> reqContext = CastUtils.cast((Map<?, ?>) invContext.get(REQUEST_CONTEXT));
    int bodyIndex = body != null ? (Integer) reqContext.get("BODY_INDEX") : -1;
    OperationResourceInfo ori =
        (OperationResourceInfo) reqContext.get(OperationResourceInfo.class.getName());
    return doChainedInvocation(newRequestURI, headers, ori, body, bodyIndex, exchange, invContext);
  }

  protected Object handleResponse(Message outMessage, Class<?> serviceCls) throws Throwable {
    try {
      Response r = setResponseBuilder(outMessage, outMessage.getExchange()).build();
      ((ResponseImpl) r).setOutMessage(outMessage);
      getState().setResponse(r);

      Method method = outMessage.getExchange().get(Method.class);
      checkResponse(method, r, outMessage);
      if (method.getReturnType() == Void.class || method.getReturnType() == Void.TYPE) {
        return null;
      }
      if (method.getReturnType() == Response.class
          && (r.getEntity() == null
              || InputStream.class.isAssignableFrom(r.getEntity().getClass())
                  && ((InputStream) r.getEntity()).available() == 0)) {
        return r;
      }
      if (PropertyUtils.isTrue(
          super.getConfiguration().getResponseContext().get(BUFFER_PROXY_RESPONSE))) {
        r.bufferEntity();
      }

      Class<?> returnType = method.getReturnType();
      Type genericType =
          InjectionUtils.processGenericTypeIfNeeded(
              serviceCls, returnType, method.getGenericReturnType());
      returnType = InjectionUtils.updateParamClassToTypeIfNeeded(returnType, genericType);
      return readBody(r, outMessage, returnType, genericType, method.getDeclaredAnnotations());
    } finally {
      ClientProviderFactory.getInstance(outMessage).clearThreadLocalProxies();
    }
  }

  public Object getInvocationHandler() {
    return this;
  }

  protected static void reportInvalidResourceMethod(Method m, String name) {
    org.apache.cxf.common.i18n.Message errorMsg =
        new org.apache.cxf.common.i18n.Message(
            name, BUNDLE, m.getDeclaringClass().getName(), m.getName());
    LOG.severe(errorMsg.toString());
    throw new ProcessingException(errorMsg.toString());
  }

  protected static Annotation[] getMethodAnnotations(Method aMethod, int bodyIndex) {
    return aMethod == null || bodyIndex == -1
        ? new Annotation[0]
        : aMethod.getParameterAnnotations()[bodyIndex];
  }

  private class BodyWriter extends AbstractBodyWriter {

    protected void doWriteBody(
        Message outMessage, Object body, Type bodyType, Annotation[] customAnns, OutputStream os)
        throws Fault {

      OperationResourceInfo ori = outMessage.getContent(OperationResourceInfo.class);
      if (ori == null) {
        return;
      }

      Method method = ori.getMethodToInvoke();
      int bodyIndex = (Integer) outMessage.get("BODY_INDEX");

      Annotation[] anns =
          customAnns != null
              ? customAnns
              : getMethodAnnotations(ori.getAnnotatedMethod(), bodyIndex);
      try {
        if (bodyIndex != -1) {
          Class<?> paramClass = method.getParameterTypes()[bodyIndex];
          Class<?> bodyClass =
              paramClass.isAssignableFrom(body.getClass()) ? paramClass : body.getClass();
          Type genericType = method.getGenericParameterTypes()[bodyIndex];
          if (bodyType != null) {
            genericType = bodyType;
          }
          genericType =
              InjectionUtils.processGenericTypeIfNeeded(
                  ori.getClassResourceInfo().getServiceClass(), bodyClass, genericType);
          bodyClass = InjectionUtils.updateParamClassToTypeIfNeeded(bodyClass, genericType);
          writeBody(body, outMessage, bodyClass, genericType, anns, os);
        } else {
          Type paramType = body.getClass();
          if (bodyType != null) {
            paramType = bodyType;
          }
          writeBody(body, outMessage, body.getClass(), paramType, anns, os);
        }
      } catch (Exception ex) {
        throw new Fault(ex);
      }
    }
  }

  private static class BeanPair {
    private Object value;
    private Annotation[] anns;

    BeanPair(Object value, Annotation[] anns) {
      this.value = value;
      this.anns = anns;
    }

    public Object getValue() {
      return value;
    }

    public Annotation[] getAnns() {
      return anns;
    }
  }
}