View Javadoc
1   /*
2    * Copyright 2005-2010 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.util.HashMap;
20  import java.util.Map;
21  import java.util.Properties;
22  
23  import org.springframework.beans.BeansException;
24  import org.springframework.context.ApplicationContextException;
25  import org.springframework.util.StringUtils;
26  import org.springframework.ws.context.MessageContext;
27  
28  /**
29   * Abstract base class for endpoint mapping that are based on a <code>Map</code>. Provides mappings of application
30   * context beans as well as a settable map.
31   * <p/>
32   * Subclasses determine the exact nature of the key in the enpoint map; this can be a qualified name, a SOAP Header, the
33   * result of a XPath validation. The values are always endpoint objects, or bean names of endpoint objects.
34   *
35   * @author Arjen Poutsma
36   * @since 1.0.0
37   */
38  public abstract class AbstractMapBasedEndpointMapping extends AbstractEndpointMapping {
39  
40      private boolean lazyInitEndpoints = false;
41  
42      private boolean registerBeanNames = false;
43  
44      private final Map<String, Object> endpointMap = new HashMap<String, Object>();
45  
46      // holds mappings set via setEndpointMap and setMappings
47      private Map<String, Object> temporaryEndpointMap = new HashMap<String, Object>();
48  
49      /**
50       * Set whether to lazily initialize endpoints. Only applicable to singleton endpoints, as prototypes are always
51       * lazily initialized. Default is <code>false</code>, as eager initialization allows for more efficiency through
52       * referencing the controller objects directly.
53       * <p/>
54       * If you want to allow your endpoints to be lazily initialized, make them "lazy-init" and set this flag to
55       * <code>true</code>. Just making them "lazy-init" will not work, as they are initialized through the references
56       * from the endpoint mapping in this case.
57       */
58      public void setLazyInitEndpoints(boolean lazyInitEndpoints) {
59          this.lazyInitEndpoints = lazyInitEndpoints;
60      }
61  
62      /**
63       * Set whether to register bean names found in the application context. Setting this to <code>true</code> will
64       * register all beans found in the application context under their name. Default is <code>false</code>.
65       */
66      public final void setRegisterBeanNames(boolean registerBeanNames) {
67          this.registerBeanNames = registerBeanNames;
68      }
69  
70      /**
71       * Sets a Map with keys and endpoint beans as values. The nature of the keys in the given map depends on the exact
72       * subclass used. They can be qualified names, for instance, or mime headers.
73       *
74       * @throws IllegalArgumentException if the endpoint is invalid
75       */
76      public final void setEndpointMap(Map<String, Object> endpointMap) {
77          temporaryEndpointMap.putAll(endpointMap);
78      }
79  
80      /**
81       * Maps keys to endpoint bean names. The nature of the property names depends on the exact subclass used. They can
82       * be qualified names, for instance, or mime headers.
83       */
84      public void setMappings(Properties mappings) {
85          for (Map.Entry<Object, Object> entry : mappings.entrySet()) {
86              if (entry.getKey() instanceof String) {
87                  temporaryEndpointMap.put((String) entry.getKey(), entry.getValue());
88              }
89          }
90      }
91  
92      /** Validates the given endpoint key. Should return <code>true</code> is the given string is valid. */
93      protected abstract boolean validateLookupKey(String key);
94  
95      /**
96       * Returns the the endpoint key for the given message context. Returns <code>null</code> if a key cannot be found.
97       *
98       * @return the registration key; or <code>null</code>
99       */
100     protected abstract String getLookupKeyForMessage(MessageContext messageContext) throws Exception;
101 
102     /**
103      * Lookup an endpoint for the given message. The extraction of the endpoint key is delegated to the concrete
104      * subclass.
105      *
106      * @return the looked up endpoint, or <code>null</code>
107      */
108     @Override
109     protected final Object getEndpointInternal(MessageContext messageContext) throws Exception {
110         String key = getLookupKeyForMessage(messageContext);
111         if (!StringUtils.hasLength(key)) {
112             return null;
113         }
114         if (logger.isDebugEnabled()) {
115             logger.debug("Looking up endpoint for [" + key + "]");
116         }
117         return lookupEndpoint(key);
118     }
119 
120     /**
121      * Looks up an endpoint instance for the given keys. All keys are tried in order.
122      *
123      * @param key key the beans are mapped to
124      * @return the associated endpoint instance, or <code>null</code> if not found
125      */
126     protected Object lookupEndpoint(String key) {
127         return endpointMap.get(key);
128     }
129 
130     /**
131      * Register the given endpoint instance under the registration key.
132      *
133      * @param key      the string representation of the registration key
134      * @param endpoint the endpoint instance
135      * @throws org.springframework.beans.BeansException
136      *          if the endpoint could not be registered
137      */
138     protected void registerEndpoint(String key, Object endpoint) throws BeansException {
139         Object mappedEndpoint = endpointMap.get(key);
140         if (mappedEndpoint != null) {
141             throw new ApplicationContextException("Cannot map endpoint [" + endpoint + "] on registration key [" + key +
142                     "]: there's already endpoint [" + mappedEndpoint + "] mapped");
143         }
144         if (!lazyInitEndpoints && endpoint instanceof String) {
145             String endpointName = (String) endpoint;
146             endpoint = resolveStringEndpoint(endpointName);
147         }
148         if (endpoint == null) {
149             throw new ApplicationContextException("Could not find endpoint for key [" + key + "]");
150         }
151         endpointMap.put(key, endpoint);
152         if (logger.isDebugEnabled()) {
153             logger.debug("Mapped key [" + key + "] onto endpoint [" + endpoint + "]");
154         }
155     }
156 
157     /**
158      * Registers annd checks the set endpoints. Checks the beans set through <code>setEndpointMap</code> and
159      * <code>setMappings</code>, and registers the bean names found in the application context, if
160      * <code>registerBeanNames</code> is set to <code>true</code>.
161      *
162      * @throws ApplicationContextException if either of the endpoints defined via <code>setEndpointMap</code> or
163      *                                     <code>setMappings</code> is invalid
164      * @see #setEndpointMap(java.util.Map)
165      * @see #setMappings(java.util.Properties)
166      * @see #setRegisterBeanNames(boolean)
167      */
168     @Override
169     protected final void initApplicationContext() throws BeansException {
170         super.initApplicationContext();
171         for (String key : temporaryEndpointMap.keySet()) {
172             Object endpoint = temporaryEndpointMap.get(key);
173             if (!validateLookupKey(key)) {
174                 throw new ApplicationContextException("Invalid key [" + key + "] for endpoint [" + endpoint + "]");
175             }
176             registerEndpoint(key, endpoint);
177         }
178         temporaryEndpointMap = null;
179         if (registerBeanNames) {
180             if (logger.isDebugEnabled()) {
181                 logger.debug("Looking for endpoint mappings in application context: [" + getApplicationContext() + "]");
182             }
183             String[] beanNames = getApplicationContext().getBeanDefinitionNames();
184             for (String beanName : beanNames) {
185                 if (validateLookupKey(beanName)) {
186                     registerEndpoint(beanName, beanName);
187                 }
188                 String[] aliases = getApplicationContext().getAliases(beanName);
189                 for (String aliase : aliases) {
190                     if (validateLookupKey(aliase)) {
191                         registerEndpoint(aliase, beanName);
192                     }
193                 }
194             }
195         }
196     }
197 
198 }