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.core.JdkVersion;
27  import org.springframework.util.Assert;
28  import org.springframework.util.StringUtils;
29  import org.springframework.ws.context.MessageContext;
30  import org.springframework.ws.server.endpoint.MethodEndpoint;
31  
32  /**
33   * Abstract base class for {@link MethodEndpoint} mappings.
34   * <p/>
35   * Subclasses typically implement {@link org.springframework.beans.factory.config.BeanPostProcessor} to look for beans
36   * that qualify as enpoint. The methods of this bean are then registered under a specific key with {@link
37   * #registerEndpoint(String,MethodEndpoint)}.
38   *
39   * @author Arjen Poutsma
40   * @since 1.0.0
41   */
42  public abstract class AbstractMethodEndpointMapping extends AbstractEndpointMapping {
43  
44      /** Keys are Strings, values are {@link MethodEndpoint}s. */
45      private final Map endpointMap = new HashMap();
46  
47      /**
48       * Lookup an endpoint for the given message. The extraction of the endpoint key is delegated to the concrete
49       * subclass.
50       *
51       * @return the looked up endpoint, or <code>null</code>
52       * @see #getLookupKeyForMessage(MessageContext)
53       */
54      protected Object getEndpointInternal(MessageContext messageContext) throws Exception {
55          String key = getLookupKeyForMessage(messageContext);
56          if (!StringUtils.hasLength(key)) {
57              return null;
58          }
59          if (logger.isDebugEnabled()) {
60              logger.debug("Looking up endpoint for [" + key + "]");
61          }
62          return lookupEndpoint(key);
63      }
64  
65      /**
66       * Returns the the endpoint keys for the given message context.
67       *
68       * @return the registration keys
69       */
70      protected abstract String getLookupKeyForMessage(MessageContext messageContext) throws Exception;
71  
72      /**
73       * Looks up an endpoint instance for the given keys. All keys are tried in order.
74       *
75       * @param key key the beans are mapped to
76       * @return the associated endpoint instance, or <code>null</code> if not found
77       */
78      protected MethodEndpoint lookupEndpoint(String key) {
79          return (MethodEndpoint) endpointMap.get(key);
80      }
81  
82      /**
83       * Register the given endpoint instance under the key.
84       *
85       * @param key      the lookup key
86       * @param endpoint the method endpoint instance
87       * @throws BeansException if the endpoint could not be registered
88       */
89      protected void registerEndpoint(String key, MethodEndpoint endpoint) throws BeansException {
90          Object mappedEndpoint = endpointMap.get(key);
91          if (mappedEndpoint != null) {
92              throw new ApplicationContextException("Cannot map endpoint [" + endpoint + "] on registration key [" + key +
93                      "]: there's already endpoint [" + mappedEndpoint + "] mapped");
94          }
95          if (endpoint == null) {
96              throw new ApplicationContextException("Could not find endpoint for key [" + key + "]");
97          }
98          endpointMap.put(key, endpoint);
99          if (logger.isDebugEnabled()) {
100             logger.debug("Mapped key [" + key + "] onto endpoint [" + endpoint + "]");
101         }
102     }
103 
104     /**
105      * Helper method that registers the methods of the given bean. This method iterates over the methods of the bean,
106      * and calls {@link #getLookupKeyForMethod(Method)} for each. If this returns a string, the method is registered
107      * using {@link #registerEndpoint(String,MethodEndpoint)}.
108      *
109      * @see #getLookupKeyForMethod(Method)
110      */
111     protected void registerMethods(Object endpoint) {
112         Assert.notNull(endpoint, "'endpoint' must not be null");
113         Method[] methods = getEndpointClass(endpoint).getMethods();
114         for (int i = 0; i < methods.length; i++) {
115             if (JdkVersion.getMajorJavaVersion() >= JdkVersion.JAVA_15 && methods[i].isSynthetic() ||
116                     methods[i].getDeclaringClass().equals(Object.class)) {
117                 continue;
118             }
119             String key = getLookupKeyForMethod(methods[i]);
120             if (StringUtils.hasLength(key)) {
121                 registerEndpoint(key, new MethodEndpoint(endpoint, methods[i]));
122             }
123         }
124     }
125 
126     /**
127      * Returns the the endpoint keys for the given method. Returns <code>null</code> if the method is not to be
128      * registered, which is the default.
129      *
130      * @param method the method
131      * @return a registration key, or <code>null</code> if the method is not to be registered
132      */
133     protected String getLookupKeyForMethod(Method method) {
134         return null;
135     }
136 
137     /**
138      * Return the class or interface to use for method reflection.
139      * <p/>
140      * Default implementation delegates to {@link AopUtils#getTargetClass(Object)}.
141      *
142      * @param endpoint the bean instance (might be an AOP proxy)
143      * @return the bean class to expose
144      */
145     protected Class getEndpointClass(Object endpoint) {
146         return AopUtils.getTargetClass(endpoint);
147     }
148 
149 }