001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.interceptor;
019:
020: import java.io.UnsupportedEncodingException;
021: import java.lang.reflect.Method;
022: import java.net.URLDecoder;
023: import java.util.Arrays;
024: import java.util.Collection;
025: import java.util.Iterator;
026: import java.util.LinkedHashMap;
027: import java.util.List;
028: import java.util.Map;
029: import java.util.ResourceBundle;
030: import java.util.logging.Level;
031: import java.util.logging.Logger;
032:
033: import org.apache.cxf.common.i18n.BundleUtils;
034: import org.apache.cxf.common.util.CollectionUtils;
035: import org.apache.cxf.common.util.PrimitiveUtils;
036: import org.apache.cxf.common.util.StringUtils;
037: import org.apache.cxf.frontend.MethodDispatcher;
038: import org.apache.cxf.message.Message;
039: import org.apache.cxf.message.MessageContentsList;
040: import org.apache.cxf.phase.Phase;
041: import org.apache.cxf.service.Service;
042: import org.apache.cxf.service.model.BindingOperationInfo;
043: import org.apache.cxf.service.model.MessagePartInfo;
044: import org.apache.cxf.service.model.OperationInfo;
045: import org.apache.cxf.service.model.ServiceModelUtil;
046:
047: public class URIMappingInterceptor extends
048: AbstractInDatabindingInterceptor {
049:
050: private static final Logger LOG = Logger
051: .getLogger(URIMappingInterceptor.class.getName());
052: private static final ResourceBundle BUNDLE = BundleUtils
053: .getBundle(URIMappingInterceptor.class);
054:
055: public URIMappingInterceptor() {
056: super (Phase.UNMARSHAL);
057: }
058:
059: public void handleMessage(Message message) throws Fault {
060: String method = (String) message
061: .get(Message.HTTP_REQUEST_METHOD);
062: if (LOG.isLoggable(Level.FINE)) {
063: LOG.fine("Invoking HTTP method " + method);
064: }
065: if (!isGET(message)) {
066: if (LOG.isLoggable(Level.FINE)) {
067: LOG.log(Level.FINE,
068: "URIMappingInterceptor can only handle HTTP GET, not HTTP "
069: + method);
070: }
071: return;
072: }
073:
074: String opName = getOperationName(message);
075: if (LOG.isLoggable(Level.FINE)) {
076: LOG.fine("URIMappingInterceptor get operation: " + opName);
077: }
078: BindingOperationInfo op = ServiceModelUtil.getOperation(message
079: .getExchange(), opName);
080:
081: if (op == null || opName == null || op.getName() == null
082: || StringUtils.isEmpty(op.getName().getLocalPart())
083: || !opName.equals(op.getName().getLocalPart())) {
084: throw new Fault(new org.apache.cxf.common.i18n.Message(
085: "NO_OPERATION", BUNDLE, opName));
086: }
087: message.getExchange().put(BindingOperationInfo.class, op);
088: MessageContentsList params = getParameters(message, op);
089: message.setContent(List.class, params);
090: }
091:
092: private Method getMethod(Message message,
093: BindingOperationInfo operation) {
094: MethodDispatcher md = (MethodDispatcher) message.getExchange()
095: .get(Service.class).get(
096: MethodDispatcher.class.getName());
097: return md.getMethod(operation);
098: }
099:
100: private boolean isFixedParameterOrder(Message message) {
101: // Default value is false
102: Boolean order = (Boolean) message
103: .get(Message.FIXED_PARAMETER_ORDER);
104: return order != null && order;
105: }
106:
107: protected Map<String, String> keepInOrder(
108: Map<String, String> params, OperationInfo operation,
109: List<String> order) {
110: if (params == null || params.size() == 0) {
111: return params;
112: }
113:
114: if (order == null || order.size() == 0) {
115: return params;
116: }
117:
118: Map<String, String> orderedParameters = new LinkedHashMap<String, String>();
119: for (String name : order) {
120: orderedParameters.put(name, params.get(name));
121: }
122:
123: if (order.size() != params.size()) {
124: LOG.info(order.size()
125: + " parameters definded in WSDL but found "
126: + params.size() + " in request!");
127: Collection rest = CollectionUtils.diff(order, params
128: .keySet());
129: if (rest != null && rest.size() > 0) {
130: LOG.info("Set the following parameters to null: "
131: + rest);
132: for (Iterator iter = rest.iterator(); iter.hasNext();) {
133: String key = (String) iter.next();
134: orderedParameters.put(key, null);
135: }
136: }
137: }
138:
139: return orderedParameters;
140: }
141:
142: protected MessageContentsList getParameters(Message message,
143: BindingOperationInfo operation) {
144: MessageContentsList parameters = new MessageContentsList();
145: Map<String, String> queries = getQueries(message);
146:
147: if (!isFixedParameterOrder(message)) {
148: boolean emptyQueries = CollectionUtils.isEmpty(queries
149: .values());
150:
151: List<String> names = ServiceModelUtil
152: .getOperationInputPartNames(operation
153: .getOperationInfo());
154: queries = keepInOrder(queries,
155: operation.getOperationInfo(), names);
156: if (!emptyQueries
157: && CollectionUtils.isEmpty(queries.values())) {
158: throw new Fault(new org.apache.cxf.common.i18n.Message(
159: "ORDERED_PARAM_REQUIRED", BUNDLE, names
160: .toString()));
161: }
162: }
163:
164: Method method = getMethod(message, operation);
165:
166: Class[] types = method.getParameterTypes();
167:
168: for (String key : queries.keySet()) {
169: MessagePartInfo inf = null;
170: for (MessagePartInfo p : operation.getOperationInfo()
171: .getInput().getMessageParts()) {
172: if (p.getConcreteName().getLocalPart().equals(key)) {
173: inf = p;
174: break;
175: }
176: }
177: if (inf == null && operation.isUnwrappedCapable()) {
178: for (MessagePartInfo p : operation
179: .getUnwrappedOperation().getOperationInfo()
180: .getInput().getMessageParts()) {
181: if (p.getConcreteName().getLocalPart().equals(key)) {
182: inf = p;
183: break;
184: }
185: }
186: }
187: int idx = 0;
188: if (inf != null) {
189: idx = inf.getIndex();
190: }
191: Class<?> type = types[idx];
192:
193: if (type == null) {
194: LOG
195: .warning("URIMappingInterceptor MessagePartInfo NULL ");
196: throw new Fault(new org.apache.cxf.common.i18n.Message(
197: "NO_PART_FOUND", BUNDLE, "index: " + idx
198: + " on key " + key));
199: }
200:
201: // TODO check the parameter name here
202: Object param = null;
203:
204: if (type != null && type.isPrimitive()
205: && queries.get(key) != null) {
206: param = PrimitiveUtils.read(queries.get(key), type);
207: } else {
208: param = queries.get(key);
209: }
210: parameters.set(idx, param);
211:
212: idx = parameters.size();
213: }
214: return parameters;
215: }
216:
217: private String uriDecode(String query) {
218: try {
219: query = URLDecoder.decode(query, "UTF-8");
220: } catch (UnsupportedEncodingException e) {
221: LOG.warning(query + " can not be decoded: "
222: + e.getMessage());
223: }
224: return query;
225: }
226:
227: protected Map<String, String> getQueries(Message message) {
228: Map<String, String> queries = new LinkedHashMap<String, String>();
229: String query = (String) message.get(Message.QUERY_STRING);
230: if (!StringUtils.isEmpty(query)) {
231: List<String> parts = Arrays.asList(query.split("&"));
232: for (String part : parts) {
233: String[] keyValue = part.split("=");
234: queries.put(keyValue[0], uriDecode(keyValue[1]));
235: }
236: return queries;
237: }
238:
239: String rest = getRest(message);
240: List<String> parts = StringUtils.getParts(rest, "/");
241:
242: for (int i = 1; i < parts.size(); i += 2) {
243: if (i + 1 > parts.size()) {
244: queries.put(parts.get(i), null);
245: } else {
246: queries.put(parts.get(i), uriDecode(parts.get(i + 1)));
247: }
248: }
249: return queries;
250: }
251:
252: private String getBasePath(Message message) {
253: return (String) message.get(Message.BASE_PATH);
254: }
255:
256: private String getRest(Message message) {
257: String path = (String) message.get(Message.PATH_INFO);
258: String basePath = getBasePath(message);
259: return StringUtils.diff(path, basePath);
260: }
261:
262: protected String getOperationName(Message message) {
263: String rest = getRest(message);
264: String opName = StringUtils.getFirstNotEmpty(rest, "/");
265: if (opName.indexOf("?") != -1) {
266: opName = opName.split("\\?")[0];
267: }
268:
269: return opName;
270: }
271: }
|