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 015package org.apache.tapestry.spec; 016 017import java.util.Collections; 018import java.util.HashMap; 019import java.util.Iterator; 020import java.util.Map; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024import org.apache.hivemind.ApplicationRuntimeException; 025import org.apache.hivemind.ClassResolver; 026import org.apache.hivemind.util.PropertyUtils; 027import org.apache.tapestry.Tapestry; 028import 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 038public 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}