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