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 }