001 // Copyright 2004, 2005 The Apache Software Foundation
002 //
003 // Licensed under the Apache License, Version 2.0 (the "License");
004 // you may not use this file except in compliance with the License.
005 // You may obtain a copy of the License at
006 //
007 // http://www.apache.org/licenses/LICENSE-2.0
008 //
009 // Unless required by applicable law or agreed to in writing, software
010 // distributed under the License is distributed on an "AS IS" BASIS,
011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012 // See the License for the specific language governing permissions and
013 // limitations under the License.
014
015 package org.apache.tapestry.spec;
016
017 import java.util.Collections;
018 import java.util.HashMap;
019 import java.util.Iterator;
020 import java.util.Map;
021
022 import org.apache.commons.logging.Log;
023 import org.apache.commons.logging.LogFactory;
024 import org.apache.hivemind.ApplicationRuntimeException;
025 import org.apache.hivemind.ClassResolver;
026 import org.apache.hivemind.util.PropertyUtils;
027 import org.apache.tapestry.Tapestry;
028 import org.apache.tapestry.coerce.ValueConverter;
029
030 /**
031 * Defines an "extension", which is much like a helper bean, but is part of a library or application
032 * specification (and has the same lifecycle as the application).
033 *
034 * @author Howard Lewis Ship
035 * @since 2.2
036 */
037
038 public class ExtensionSpecification extends LocatablePropertyHolder implements
039 IExtensionSpecification
040 {
041 private static final Log LOG = LogFactory.getLog(ExtensionSpecification.class);
042
043 private String _className;
044
045 protected Map _configuration = new HashMap();
046
047 private boolean _immediate;
048
049 /** @since 4.0 */
050
051 private ClassResolver _resolver;
052
053 /** @since 4.0 */
054 private ValueConverter _converter;
055
056 /** @since 4.0 */
057 public ExtensionSpecification(ClassResolver resolver, ValueConverter valueConverter)
058 {
059 _resolver = resolver;
060 _converter = valueConverter;
061 }
062
063 public String getClassName()
064 {
065 return _className;
066 }
067
068 public void setClassName(String className)
069 {
070 _className = className;
071 }
072
073 public void addConfiguration(String propertyName, String value)
074 {
075 if (_configuration.containsKey(propertyName))
076 throw new IllegalArgumentException(Tapestry.format(
077 "ExtensionSpecification.duplicate-property",
078 this,
079 propertyName));
080
081 _configuration.put(propertyName, value);
082 }
083
084 /**
085 * Returns an immutable Map of the configuration; keyed on property name, with values as
086 * properties to assign.
087 */
088
089 public Map getConfiguration()
090 {
091 return Collections.unmodifiableMap(_configuration);
092 }
093
094 /**
095 * Invoked to instantiate an instance of the extension and return it. It also configures
096 * properties of the extension.
097 */
098
099 public Object instantiateExtension()
100 {
101 if (LOG.isDebugEnabled())
102 LOG.debug("Instantiating extension class " + _className + ".");
103
104 Class extensionClass = null;
105 Object result = null;
106
107 try
108 {
109 extensionClass = _resolver.findClass(_className);
110 }
111 catch (Exception ex)
112 {
113 throw new ApplicationRuntimeException(Tapestry.format(
114 "ExtensionSpecification.bad-class",
115 _className), getLocation(), ex);
116 }
117
118 result = instantiateInstance(extensionClass, result);
119
120 initializeProperties(result);
121
122 return result;
123 }
124
125 private void initializeProperties(Object extension)
126 {
127
128 Iterator i = _configuration.entrySet().iterator();
129 while (i.hasNext())
130 {
131 Map.Entry entry = (Map.Entry) i.next();
132
133 String propertyName = (String) entry.getKey();
134 String textValue = (String) entry.getValue();
135
136 try
137 {
138 Class propertyType = PropertyUtils.getPropertyType(extension, propertyName);
139
140 Object objectValue = _converter.coerceValue(textValue, propertyType);
141
142 PropertyUtils.write(extension, propertyName, objectValue);
143 }
144 catch (Exception ex)
145 {
146 throw new ApplicationRuntimeException(ex.getMessage(), getLocation(), ex);
147 }
148 }
149 }
150
151 private Object instantiateInstance(Class extensionClass, Object result)
152 {
153 try
154 {
155 result = extensionClass.newInstance();
156 }
157 catch (Exception ex)
158 {
159 throw new ApplicationRuntimeException(ex.getMessage(), getLocation(), ex);
160 }
161
162 return result;
163 }
164
165 public String toString()
166 {
167 StringBuffer buffer = new StringBuffer("ExtensionSpecification@");
168 buffer.append(Integer.toHexString(hashCode()));
169 buffer.append('[');
170 buffer.append(_className);
171
172 if (_configuration != null)
173 {
174 buffer.append(' ');
175 buffer.append(_configuration);
176 }
177
178 buffer.append(']');
179
180 return buffer.toString();
181 }
182
183 /**
184 * Returns true if the extensions should be instantiated immediately after the containing
185 * {@link org.apache.tapestry.spec.LibrarySpecification}if parsed. Non-immediate extensions are
186 * instantiated only as needed.
187 */
188
189 public boolean isImmediate()
190 {
191 return _immediate;
192 }
193
194 public void setImmediate(boolean immediate)
195 {
196 _immediate = immediate;
197 }
198
199 }