View Javadoc

1   /*
2    * Copyright 2007 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.server.endpoint.mapping;
18  
19  import java.lang.reflect.Method;
20  import java.util.HashMap;
21  import java.util.Map;
22  
23  import org.springframework.aop.support.AopUtils;
24  import org.springframework.beans.BeansException;
25  import org.springframework.context.ApplicationContextException;
26  import org.springframework.util.Assert;
27  import org.springframework.util.ClassUtils;
28  import org.springframework.util.ReflectionUtils;
29  import org.springframework.util.StringUtils;
30  import org.springframework.ws.context.MessageContext;
31  import org.springframework.ws.server.endpoint.MethodEndpoint;
32  
33  /**
34   * Abstract base class for {@link MethodEndpoint} mappings.
35   * <p/>
36   * Subclasses typically implement {@link org.springframework.beans.factory.config.BeanPostProcessor} to look for beans
37   * that qualify as enpoint. The methods of this bean are then registered under a specific key with {@link
38   * #registerEndpoint(String,MethodEndpoint)}.
39   *
40   * @author Arjen Poutsma
41   * @since 1.0.0
42   */
43  public abstract class AbstractMethodEndpointMapping extends AbstractEndpointMapping {
44  
45      /** Keys are Strings, values are {@link MethodEndpoint}s. */
46      private final Map endpointMap = new HashMap();
47  
48      /**
49       * Lookup an endpoint for the given message. The extraction of the endpoint key is delegated to the concrete
50       * subclass.
51       *
52       * @return the looked up endpoint, or <code>null</code>
53       * @see #getLookupKeyForMessage(MessageContext)
54       */
55      protected Object getEndpointInternal(MessageContext messageContext) throws Exception {
56          String key = getLookupKeyForMessage(messageContext);
57          if (!StringUtils.hasLength(key)) {
58              return null;
59          }
60          if (logger.isDebugEnabled()) {
61              logger.debug("Looking up endpoint for [" + key + "]");
62          }
63          return lookupEndpoint(key);
64      }
65  
66      /**
67       * Returns the the endpoint keys for the given message context.
68       *
69       * @return the registration keys
70       */
71      protected abstract String getLookupKeyForMessage(MessageContext messageContext) throws Exception;
72  
73      /**
74       * Looks up an endpoint instance for the given keys. All keys are tried in order.
75       *
76       * @param key key the beans are mapped to
77       * @return the associated endpoint instance, or <code>null</code> if not found
78       */
79      protected MethodEndpoint lookupEndpoint(String key) {
80          return (MethodEndpoint) endpointMap.get(key);
81      }
82  
83      /**
84       * Register the given endpoint instance under the key.
85       *
86       * @param key      the lookup key
87       * @param endpoint the method endpoint instance
88       * @throws BeansException if the endpoint could not be registered
89       */
90      protected void registerEndpoint(String key, MethodEndpoint endpoint) throws BeansException {
91          Object mappedEndpoint = endpointMap.get(key);
92          if (mappedEndpoint != null) {
93              throw new ApplicationContextException("Cannot map endpoint [" + endpoint + "] on registration key [" + key +
94                      "]: there's already endpoint [" + mappedEndpoint + "] mapped");
95          }
96          if (endpoint == null) {
97              throw new ApplicationContextException("Could not find endpoint for key [" + key + "]");
98          }
99          endpointMap.put(key, endpoint);
100         if (logger.isDebugEnabled()) {
101             logger.debug("Mapped key [" + key + "] onto endpoint [" + endpoint + "]");
102         }
103     }
104 
105     /**
106      * Helper method that registers the methods of the given bean. This method iterates over the methods of the bean,
107      * and calls {@link #getLookupKeyForMethod(Method)} for each. If this returns a string, the method is registered
108      * using {@link #registerEndpoint(String,MethodEndpoint)}.
109      *
110      * @see #getLookupKeyForMethod(Method)
111      */
112     protected void registerMethods(final Object endpoint) {
113         Assert.notNull(endpoint, "'endpoint' must not be null");
114         Class endpointClass = getEndpointClass(endpoint);
115         ReflectionUtils.doWithMethods(endpointClass, new ReflectionUtils.MethodCallback() {
116 
117             public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
118                 String key = getLookupKeyForMethod(method);
119                 if (StringUtils.hasLength(key)) {
120                     registerEndpoint(key, new MethodEndpoint(endpoint, method));
121                 }
122             }
123         });
124     }
125 
126     /**
127      * Helper method that registers the methods of the given class. This method iterates over the methods of the class,
128      * and calls {@link #getLookupKeyForMethod(Method)} for each. If this returns a string, the method is registered
129      * using {@link #registerEndpoint(String,MethodEndpoint)}.
130      *
131      * @see #getLookupKeyForMethod(Method)
132      */
133     protected void registerMethods(final String beanName) {
134         Assert.hasText(beanName, "'beanName' must not be empty");
135         Class endpointClass = getApplicationContext().getType(beanName);
136         ReflectionUtils.doWithMethods(endpointClass, new ReflectionUtils.MethodCallback() {
137 
138             public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
139                 String key = getLookupKeyForMethod(method);
140                 if (StringUtils.hasLength(key)) {
141                     registerEndpoint(key, new MethodEndpoint(beanName, getApplicationContext(), method));
142                 }
143             }
144 
145         });
146     }
147 
148     /**
149      * Returns the the endpoint keys for the given method. Returns <code>null</code> if the method is not to be
150      * registered, which is the default.
151      *
152      * @param method the method
153      * @return a registration key, or <code>null</code> if the method is not to be registered
154      */
155     protected String getLookupKeyForMethod(Method method) {
156         return null;
157     }
158 
159     /**
160      * Return the class or interface to use for method reflection.
161      * <p/>
162      * Default implementation delegates to {@link AopUtils#getTargetClass(Object)}.
163      *
164      * @param endpoint the bean instance (might be an AOP proxy)
165      * @return the bean class to expose
166      */
167     protected Class getEndpointClass(Object endpoint) {
168         if (AopUtils.isJdkDynamicProxy(endpoint)) {
169             throw new IllegalArgumentException(ClassUtils.getShortName(getClass()) +
170                     " does not work with JDK Dynamic Proxies. " +
171                     "Please use CGLIB proxies, by setting proxy-target-class=\"true\" on the aop:aspectj-autoproxy " +
172                     "or aop:config element.");
173         }
174         return AopUtils.getTargetClass(endpoint);
175     }
176 }