001: /*
002: * Copyright 2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.ws.server.endpoint.mapping;
018:
019: import java.lang.reflect.Method;
020: import java.util.HashMap;
021: import java.util.Map;
022:
023: import org.springframework.beans.BeansException;
024: import org.springframework.context.ApplicationContextException;
025: import org.springframework.core.JdkVersion;
026: import org.springframework.util.Assert;
027: import org.springframework.util.StringUtils;
028: import org.springframework.ws.context.MessageContext;
029: import org.springframework.ws.server.endpoint.MethodEndpoint;
030:
031: /**
032: * Abstract base class for {@link MethodEndpoint} mappings.
033: * <p/>
034: * Subclasses typically implement {@link org.springframework.beans.factory.config.BeanPostProcessor} to look for beans
035: * that qualify as enpoint. The methods of this bean are then registered under a specific key with {@link
036: * #registerEndpoint(String,MethodEndpoint)}.
037: *
038: * @author Arjen Poutsma
039: * @since 1.0.0
040: */
041: public abstract class AbstractMethodEndpointMapping extends
042: AbstractEndpointMapping {
043:
044: /** Keys are Strings, values are {@link MethodEndpoint}s. */
045: private final Map endpointMap = new HashMap();
046:
047: /**
048: * Lookup an endpoint for the given message. The extraction of the endpoint key is delegated to the concrete
049: * subclass.
050: *
051: * @return the looked up endpoint, or <code>null</code>
052: * @see #getLookupKeyForMessage(MessageContext)
053: */
054: protected Object getEndpointInternal(MessageContext messageContext)
055: throws Exception {
056: String key = getLookupKeyForMessage(messageContext);
057: if (!StringUtils.hasLength(key)) {
058: return null;
059: }
060: if (logger.isDebugEnabled()) {
061: logger.debug("Looking up endpoint for [" + key + "]");
062: }
063: return lookupEndpoint(key);
064: }
065:
066: /**
067: * Returns the the endpoint keys for the given message context.
068: *
069: * @return the registration keys
070: */
071: protected abstract String getLookupKeyForMessage(
072: MessageContext messageContext) throws Exception;
073:
074: /**
075: * Looks up an endpoint instance for the given keys. All keys are tried in order.
076: *
077: * @param key key the beans are mapped to
078: * @return the associated endpoint instance, or <code>null</code> if not found
079: */
080: protected MethodEndpoint lookupEndpoint(String key) {
081: return (MethodEndpoint) endpointMap.get(key);
082: }
083:
084: /**
085: * Register the given endpoint instance under the key.
086: *
087: * @param key the lookup key
088: * @param endpoint the method endpoint instance
089: * @throws BeansException if the endpoint could not be registered
090: */
091: protected void registerEndpoint(String key, MethodEndpoint endpoint)
092: throws BeansException {
093: Object mappedEndpoint = endpointMap.get(key);
094: if (mappedEndpoint != null) {
095: throw new ApplicationContextException(
096: "Cannot map endpoint [" + endpoint
097: + "] on registration key [" + key
098: + "]: there's already endpoint ["
099: + mappedEndpoint + "] mapped");
100: }
101: if (endpoint == null) {
102: throw new ApplicationContextException(
103: "Could not find endpoint for key [" + key + "]");
104: }
105: endpointMap.put(key, endpoint);
106: if (logger.isDebugEnabled()) {
107: logger.debug("Mapped key [" + key + "] onto endpoint ["
108: + endpoint + "]");
109: }
110: }
111:
112: /**
113: * Helper method that registers the methods of the given bean. This method iterates over the methods of the bean,
114: * and calls {@link #getLookupKeyForMethod(Method)} for each. If this returns a string, the method is registered
115: * using {@link #registerEndpoint(String,MethodEndpoint)}.
116: *
117: * @see #getLookupKeyForMethod(Method)
118: */
119: protected void registerMethods(Object endpoint) {
120: Assert.notNull(endpoint, "'endpoint' must not be null");
121: Method[] methods = getEndpointClass(endpoint).getMethods();
122: for (int i = 0; i < methods.length; i++) {
123: if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_15
124: && methods[i].isSynthetic()
125: || methods[i].getDeclaringClass().equals(
126: Object.class)) {
127: continue;
128: }
129: String key = getLookupKeyForMethod(methods[i]);
130: if (StringUtils.hasLength(key)) {
131: registerEndpoint(key, new MethodEndpoint(endpoint,
132: methods[i]));
133: }
134: }
135: }
136:
137: /**
138: * Returns the the endpoint keys for the given method. Returns <code>null</code> if the method is not to be
139: * registered, which is the default.
140: *
141: * @param method the method
142: * @return a registration key, or <code>null</code> if the method is not to be registered
143: */
144: protected String getLookupKeyForMethod(Method method) {
145: return null;
146: }
147:
148: /**
149: * Return the class or interface to use for method reflection.
150: * <p/>
151: * Default implementation returns the target class for a CGLIB proxy, and the class of the given bean else (for a
152: * JDK proxy or a plain bean class).
153: *
154: * @param endpoint the bean instance (might be an AOP proxy)
155: * @return the bean class to expose
156: */
157: protected Class getEndpointClass(Object endpoint) {
158: Class clazz = endpoint.getClass();
159: // The following is actually in Spring 2 ClassUtils.getUserClass, but since Spring-WS is Spring 1.2.9 upwards,
160: // we can't use it
161: return clazz != null && clazz.getName().indexOf("$$") != -1 ? clazz
162: .getSuperclass()
163: : clazz;
164: }
165:
166: }
|