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.binding.http.strategy;
019:
020: import java.lang.reflect.Method;
021: import java.util.logging.Logger;
022:
023: import org.apache.cxf.binding.http.URIMapper;
024: import org.apache.cxf.service.model.BindingOperationInfo;
025: import org.apache.cxf.service.model.MessagePartInfo;
026:
027: import static org.apache.cxf.binding.http.HttpConstants.DELETE;
028: import static org.apache.cxf.binding.http.HttpConstants.GET;
029: import static org.apache.cxf.binding.http.HttpConstants.POST;
030: import static org.apache.cxf.binding.http.HttpConstants.PUT;
031:
032: /**
033: * <p>
034: * Maps a BindingOperation to a URI/combination using the following rules:
035: * </p>
036: * <p>
037: * <b>GET Operations:</b> If the operation name starts with "get" and it has
038: * no parameters, it is assumed it is a get for a collection of objects. The
039: * noun after the "get" is turned into the resource name. Example: "getCustomers"
040: * is turned into "/customers".
041: * </p>
042: * <p>
043: * If the operation name starts with "get" and it takes parameters, it is taken
044: * to be a get for a singular noun. In this case the noun is pluralized, and the
045: * resource name is the pluralized noun and any additional parameters the operation
046: * takes. For the case of the operation which has a signature of
047: * "Customer getCustomer(String id)" the resource would become "/customers/{id}".
048: * </p>
049: *
050: * <p>
051: * <b>POST Operations:</b> If the operation name starts with "add" or "create"
052: * it is turned into a POST operation. In this case noun after add/create is
053: * pluralized and turned into the resource name. Example: "addCustomer(Customer)"
054: * is truned into "/customers".
055: * </p>
056: * <p>
057: * <b>PUT Operations:</b> If the operation name starts with "update"
058: * it is turned into a PUT operation. In this case the resource name is the
059: * pluralized noun after "update" and any additional XML schema primitive
060: * parameters the operation takes. Example: "updateCustomer(String id, Customer c)"
061: * becomes "/customers/{id}". The customer object does NOT become part of the
062: * resource name because it doesn't map to an XML schema primitive type such as
063: * xsd:int, xsd:string, etc.
064: * </p>
065: * <b>DELETE Operations:</b> Delete operations follow the same rules as PUT
066: * operations, except the operation name must start with either "delete" or
067: * "remove". Example: "deleteCustomer(String id,)" becomes "/customers/{id}".
068: * </p>
069: *
070: */
071: public class ConventionStrategy implements ResourceStrategy {
072: private static final Logger LOG = Logger
073: .getLogger(ConventionStrategy.class.getName());
074: private Inflector inflector = new EnglishInflector();
075:
076: public boolean map(BindingOperationInfo bop, Method m,
077: URIMapper mapper) {
078: String name = m.getName();
079: String verb;
080: String noun;
081: String resource;
082:
083: // find the most appropriate binding operation
084: BindingOperationInfo bopWithParts = bop.isUnwrappedCapable() ? bop
085: .getUnwrappedOperation()
086: : bop;
087: boolean pluralize = bopWithParts.getInput().getMessageParts()
088: .size() > 0;
089:
090: if (name.startsWith("get")) {
091: verb = GET;
092: noun = extractNoun(name, 3, pluralize);
093: resource = createResourceName(noun, bopWithParts);
094: } else if (name.startsWith("add")) {
095: verb = POST;
096: noun = extractNoun(name, 3, pluralize);
097: resource = noun;
098: } else if (name.startsWith("create")) {
099: verb = POST;
100: noun = extractNoun(name, 5, pluralize);
101: resource = noun;
102: } else if (name.startsWith("update")) {
103: verb = PUT;
104: noun = extractNoun(name, 6, pluralize);
105: resource = createResourceName(noun, bopWithParts);
106: } else if (name.startsWith("remove")
107: || name.startsWith("delete")) {
108: verb = DELETE;
109: noun = extractNoun(name, 6, pluralize);
110: resource = createResourceName(noun, bopWithParts);
111: } else {
112: verb = POST;
113: noun = name;
114: resource = noun;
115: }
116:
117: resource = '/' + resource;
118:
119: LOG.info("Mapping method " + name + " to resource " + resource
120: + " and verb " + verb);
121:
122: mapper.bind(bop, resource, verb);
123:
124: return true;
125: }
126:
127: private String createResourceName(String noun,
128: BindingOperationInfo bopWithParts) {
129: StringBuilder builder = new StringBuilder();
130: builder.append(noun);
131: for (MessagePartInfo part : bopWithParts.getInput()
132: .getMessageParts()) {
133: if (isXSDPrimitive(part)) {
134: builder.append("/{").append(
135: part.getName().getLocalPart()).append("}");
136: }
137: }
138: return builder.toString();
139: }
140:
141: private boolean isXSDPrimitive(MessagePartInfo part) {
142: String xsdNs = "http://www.w3.org/2001/XMLSchema";
143: if (!part.isElement()
144: && part.getTypeQName().getNamespaceURI().equals(xsdNs)) {
145: return true;
146: }
147:
148: // TODO: introspect xml schema object to see if the <xsd:element> is a simpleType
149: // restriction
150: return false;
151: }
152:
153: private String extractNoun(String name, int n, boolean pluralize) {
154: name = name.substring(n, n + 1).toLowerCase()
155: + name.substring(n + 1);
156:
157: if (pluralize) {
158: name = inflector.pluralize(name);
159: }
160:
161: return name;
162: }
163:
164: public Inflector getInflector() {
165: return inflector;
166: }
167:
168: public void setInflector(Inflector inflector) {
169: this.inflector = inflector;
170: }
171:
172: }
|