001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.camel.component.cxf.jaxrs;
019
020 import java.lang.reflect.Method;
021 import java.lang.reflect.ParameterizedType;
022 import java.lang.reflect.Type;
023 import java.util.Collection;
024 import java.util.List;
025 import java.util.Map;
026
027 import javax.ws.rs.core.Response;
028
029 import org.apache.camel.CamelException;
030 import org.apache.camel.Exchange;
031 import org.apache.camel.Message;
032 import org.apache.camel.component.cxf.CxfConstants;
033 import org.apache.camel.impl.DefaultProducer;
034 import org.apache.commons.logging.Log;
035 import org.apache.commons.logging.LogFactory;
036 import org.apache.cxf.jaxrs.JAXRSServiceFactoryBean;
037 import org.apache.cxf.jaxrs.client.Client;
038 import org.apache.cxf.jaxrs.client.JAXRSClientFactoryBean;
039 import org.apache.cxf.jaxrs.client.WebClient;
040
041
042 /**
043 * CxfRsProducer binds a Camel exchange to a CXF exchange, acts as a CXF
044 * JAXRS client, it will turn the normal Object invocation to a RESTful request
045 * according to resource annotation. Any response will be bound to Camel exchange.
046 */
047 public class CxfRsProducer extends DefaultProducer {
048
049 private static final Log LOG = LogFactory.getLog(CxfRsProducer.class);
050
051 JAXRSClientFactoryBean cfb;
052
053 public CxfRsProducer(CxfRsEndpoint endpoint) {
054 super(endpoint);
055 cfb = endpoint.createJAXRSClientFactoryBean();
056 }
057
058 public void process(Exchange exchange) throws Exception {
059
060 if (LOG.isTraceEnabled()) {
061 LOG.trace("Process exchange: " + exchange);
062 }
063
064 Message inMessage = exchange.getIn();
065 Boolean httpClientAPI = inMessage.getHeader(CxfConstants.CAMEL_CXF_RS_USING_HTTP_API, Boolean.class);
066 // set the value with endpoint's option
067 if (httpClientAPI == null) {
068 httpClientAPI = ((CxfRsEndpoint)getEndpoint()).isHttpClientAPI();
069 }
070 if (httpClientAPI.booleanValue()) {
071 invokeHttpClient(exchange);
072 } else {
073 invokeProxyClient(exchange);
074 }
075
076 }
077
078 @SuppressWarnings("unchecked")
079 protected void invokeHttpClient(Exchange exchange) throws Exception {
080 Message inMessage = exchange.getIn();
081 WebClient client = cfb.createWebClient();
082 String httpMethod = inMessage.getHeader(Exchange.HTTP_METHOD, String.class);
083 Class responseClass = inMessage.getHeader(CxfConstants.CAMEL_CXF_RS_RESPONSE_CLASS, Class.class);
084 Type genericType = inMessage.getHeader(CxfConstants.CAMEL_CXF_RS_RESPONSE_GENERIC_TYPE, Type.class);
085 String path = inMessage.getHeader(Exchange.HTTP_PATH, String.class);
086
087 if (LOG.isTraceEnabled()) {
088 LOG.trace("HTTP method = " + httpMethod);
089 LOG.trace("path = " + path);
090 LOG.trace("responseClass = " + responseClass);
091 }
092
093 // set the path
094 if (path != null) {
095 client.path(path);
096 }
097
098 CxfRsEndpoint cxfRsEndpoint = (CxfRsEndpoint)getEndpoint();
099 // check if there is a query map in the message header
100 Map<String, String> maps = inMessage.getHeader(CxfConstants.CAMEL_CXF_RS_QUERY_MAP, Map.class);
101 if (maps == null) {
102 maps = cxfRsEndpoint.getParameters();
103 }
104 if (maps != null) {
105 for (Map.Entry<String, String> entry : maps.entrySet()) {
106 client.query(entry.getKey(), entry.getValue());
107 }
108 }
109
110 CxfRsBinding binding = cxfRsEndpoint.getBinding();
111
112 // set the body
113 Object body = null;
114 if (!"GET".equals(httpMethod)) {
115 // need to check the request object.
116 body = binding.bindCamelMessageBodyToRequestBody(inMessage, exchange);
117 if (LOG.isTraceEnabled()) {
118 LOG.trace("Request body = " + body);
119 }
120 }
121
122 // set headers
123 client.headers(binding.bindCamelHeadersToRequestHeaders(inMessage.getHeaders(),
124 exchange));
125
126 // invoke the client
127 Object response = null;
128 if (responseClass == null || Response.class.equals(responseClass)) {
129 response = client.invoke(httpMethod, body);
130 } else if (Collection.class.isAssignableFrom(responseClass)) {
131 if (genericType instanceof ParameterizedType) {
132 // Get the collection member type first
133 Type[] actualTypeArguments = ((ParameterizedType) genericType).getActualTypeArguments();
134 response = client.invokeAndGetCollection(httpMethod, body, (Class)actualTypeArguments[0]);
135 } else {
136 throw new CamelException("Can't find the Collection member type");
137 }
138 } else {
139 response = client.invoke(httpMethod, body, responseClass);
140 }
141
142 // set response
143 if (exchange.getPattern().isOutCapable()) {
144 if (LOG.isTraceEnabled()) {
145 LOG.trace("Response body = " + response);
146 }
147 exchange.getOut().setBody(binding.bindResponseToCamelBody(response, exchange));
148 exchange.getOut().setHeaders(binding.bindResponseHeadersToCamelHeaders(response, exchange));
149 }
150 }
151
152 protected void invokeProxyClient(Exchange exchange) throws Exception {
153 Message inMessage = exchange.getIn();
154 Object[] varValues = inMessage.getHeader(CxfConstants.CAMEL_CXF_RS_VAR_VALUES, Object[].class);
155 String methodName = inMessage.getHeader(CxfConstants.OPERATION_NAME, String.class);
156 Client target = null;
157 if (varValues == null) {
158 target = cfb.create();
159 } else {
160 target = cfb.createWithValues(varValues);
161 }
162 // find out the method which we want to invoke
163 JAXRSServiceFactoryBean sfb = cfb.getServiceFactory();
164 sfb.getResourceClasses();
165 Object[] parameters = inMessage.getBody(Object[].class);
166 // get the method
167 Method method = findRightMethod(sfb.getResourceClasses(), methodName, getParameterTypes(parameters));
168 // Will send out the message to
169 // Need to deal with the sub resource class
170 Object response = method.invoke(target, parameters);
171 if (exchange.getPattern().isOutCapable()) {
172 exchange.getOut().setBody(response);
173 }
174 }
175
176 private Method findRightMethod(List<Class<?>> resourceClasses, String methodName, Class[] parameterTypes) throws NoSuchMethodException {
177 Method answer = null;
178 for (Class<?> clazz : resourceClasses) {
179 try {
180 answer = clazz.getMethod(methodName, parameterTypes);
181 } catch (NoSuchMethodException ex) {
182 // keep looking
183 } catch (SecurityException ex) {
184 // keep looking
185 }
186 if (answer != null) {
187 return answer;
188 }
189 }
190 throw new NoSuchMethodException("Can find the method " + methodName
191 + "withe these parameter " + arrayToString(parameterTypes));
192 }
193
194
195 private Class<?>[] getParameterTypes(Object[] objects) {
196 Class<?>[] answer = new Class[objects.length];
197 int i = 0;
198 for (Object obj : objects) {
199 answer[i] = obj.getClass();
200 i++;
201 }
202 return answer;
203 }
204
205 private String arrayToString(Object[] array) {
206 StringBuilder buffer = new StringBuilder("[");
207 for (Object obj : array) {
208 if (buffer.length() > 2) {
209 buffer.append(",");
210 }
211 buffer.append(obj.toString());
212 }
213 buffer.append("]");
214 return buffer.toString();
215 }
216
217 }