001: /*
002: * Copyright 2005 Joe Walker
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.directwebremoting.impl;
017:
018: import java.lang.reflect.InvocationTargetException;
019: import java.lang.reflect.Method;
020: import java.util.Collection;
021: import java.util.Collections;
022: import java.util.Map;
023: import java.util.Map.Entry;
024: import java.util.TreeMap;
025:
026: import javax.servlet.Servlet;
027:
028: import org.apache.commons.logging.LogFactory;
029: import org.apache.commons.logging.Log;
030: import org.directwebremoting.Container;
031: import org.directwebremoting.extend.ContainerConfigurationException;
032: import org.directwebremoting.util.LocalUtil;
033:
034: /**
035: * DefaultContainer is like a mini-IoC container for DWR.
036: * At least it is an IoC container by interface (check: no params that have
037: * anything to do with DWR), but it is hard coded specifically for DWR. If we
038: * want to make more of it we can later, but this is certainly not going to
039: * become a full blown IoC container.
040: * @author Joe Walker [joe at getahead dot ltd dot uk]
041: */
042: public class DefaultContainer extends AbstractContainer implements
043: Container {
044: /**
045: * Set the class that should be used to implement the given interface
046: * @param base The interface to implement
047: * @param implementation The new implementation
048: * @throws ContainerConfigurationException If the specified beans could not be used
049: */
050: public <T> void addImplementation(Class<T> base,
051: Class<? extends T> implementation) {
052: addParameter(base.getName(), implementation.getName());
053: }
054:
055: /**
056: * Add a parameter to the container as a possibility for injection
057: * @param askFor The key that will be looked up
058: * @param valueParam The value to be returned from the key lookup
059: * @throws ContainerConfigurationException If the specified beans could not be used
060: */
061: public void addParameter(String askFor, Object valueParam)
062: throws ContainerConfigurationException {
063: Object value = valueParam;
064:
065: // Maybe the value is a classname that needs instantiating
066: if (value instanceof String) {
067: try {
068: Class<?> impl = LocalUtil.classForName((String) value);
069: value = impl.newInstance();
070: } catch (ClassNotFoundException ex) {
071: // it's not a classname, leave it
072: } catch (InstantiationException ex) {
073: throw new ContainerConfigurationException(
074: "Unable to instantiate " + value);
075: } catch (IllegalAccessException ex) {
076: throw new ContainerConfigurationException(
077: "Unable to access " + value);
078: }
079: }
080:
081: // If we have an instantiated value object and askFor is an interface
082: // then we can check that one implements the other
083: if (!(value instanceof String)) {
084: try {
085: Class<?> iface = LocalUtil.classForName(askFor);
086: if (!iface.isAssignableFrom(value.getClass())) {
087: log.error("Can't cast: " + value + " to " + askFor);
088: }
089: } catch (ClassNotFoundException ex) {
090: // it's not a classname, leave it
091: }
092: }
093:
094: if (log.isDebugEnabled()) {
095: if (value instanceof String) {
096: log
097: .debug("Adding IoC setting: " + askFor + "="
098: + value);
099: } else {
100: log.debug("Adding IoC implementation: " + askFor + "="
101: + value.getClass().getName());
102: }
103: }
104:
105: beans.put(askFor, value);
106: }
107:
108: /**
109: * Retrieve a previously set parameter
110: * @param name The parameter name to retrieve
111: * @return The value of the specified parameter, or null if one is not set
112: */
113: public String getParameter(String name) {
114: Object value = beans.get(name);
115: return (value == null) ? null : value.toString();
116: }
117:
118: /**
119: * Called to indicate that we finished adding parameters.
120: * The thread safety of a large part of DWR depends on this function only
121: * being called from {@link Servlet#init(javax.servlet.ServletConfig)},
122: * where all the setup is done, and where we depend on the undocumented
123: * feature of all servlet containers that they complete the init process
124: * of a Servlet before they begin servicing requests.
125: * @see DefaultContainer#addParameter(String, Object)
126: * @noinspection UnnecessaryLabelOnContinueStatement
127: */
128: public void setupFinished() {
129: // We try to autowire each bean in turn
130: for (Entry<String, Object> entry : beans.entrySet()) {
131: // Class type = (Class) entry.getKey();
132: Object ovalue = entry.getValue();
133:
134: if (!(ovalue instanceof String)) {
135: log.debug("Trying to autowire: "
136: + ovalue.getClass().getName());
137:
138: Method[] methods = ovalue.getClass().getMethods();
139: methods: for (Method setter : methods) {
140: if (setter.getName().startsWith("set")
141: && setter.getName().length() > 3
142: && setter.getParameterTypes().length == 1) {
143: String name = Character.toLowerCase(setter
144: .getName().charAt(3))
145: + setter.getName().substring(4);
146: Class<?> propertyType = setter
147: .getParameterTypes()[0];
148:
149: // First we try auto-wire by name
150: Object setting = beans.get(name);
151: if (setting != null) {
152: if (propertyType.isAssignableFrom(setting
153: .getClass())) {
154: log.debug("- autowire-by-name: " + name
155: + "=" + setting);
156: invoke(setter, ovalue, setting);
157:
158: continue methods;
159: } else if (setting.getClass() == String.class) {
160: try {
161: Object value = LocalUtil
162: .simpleConvert(
163: (String) setting,
164: propertyType);
165:
166: log.debug("- autowire-by-name: "
167: + name + "=" + value);
168: invoke(setter, ovalue, value);
169: } catch (IllegalArgumentException ex) {
170: // Ignore - this was a speculative convert anyway
171: }
172:
173: continue methods;
174: }
175: }
176:
177: // Next we try autowire-by-type
178: Object value = beans
179: .get(propertyType.getName());
180: if (value != null) {
181: log.debug("- autowire-by-type: " + name
182: + "=" + value.getClass().getName());
183: invoke(setter, ovalue, value);
184:
185: continue methods;
186: }
187:
188: log.debug("- skipped autowire: " + name);
189: }
190: }
191: }
192: }
193:
194: callInitializingBeans();
195: }
196:
197: /**
198: * A helper to do the reflection.
199: * This helper throws away all exceptions, preferring to log.
200: * @param setter The method to invoke
201: * @param bean The object to invoke the method on
202: * @param value The value to assign to the property using the setter method
203: */
204: private static void invoke(Method setter, Object bean, Object value) {
205: try {
206: setter.invoke(bean, value);
207: } catch (IllegalArgumentException ex) {
208: log.error("- Internal error: " + ex.getMessage());
209: } catch (IllegalAccessException ex) {
210: log.error("- Permission error: " + ex.getMessage());
211: } catch (InvocationTargetException ex) {
212: log.error("- Exception during auto-wire: ", ex
213: .getTargetException());
214: }
215: }
216:
217: /* (non-Javadoc)
218: * @see org.directwebremoting.Container#getBean(java.lang.String)
219: */
220: public Object getBean(String id) {
221: return beans.get(id);
222: }
223:
224: /* (non-Javadoc)
225: * @see org.directwebremoting.Container#getBeanNames()
226: */
227: public Collection<String> getBeanNames() {
228: return Collections.unmodifiableCollection(beans.keySet());
229: }
230:
231: /**
232: * The beans that we know of indexed by type
233: */
234: protected Map<String, Object> beans = new TreeMap<String, Object>();
235:
236: /**
237: * The log stream
238: */
239: private static final Log log = LogFactory
240: .getLog(DefaultContainer.class);
241: }
|