001 /* ==================================================================== 002 * The Apache Software License, Version 1.1 003 * 004 * Copyright (c) 2003 The Apache Software Foundation. All rights 005 * reserved. 006 * 007 * Redistribution and use in source and binary forms, with or without 008 * modification, are permitted provided that the following conditions 009 * are met: 010 * 011 * 1. Redistributions of source code must retain the above copyright 012 * notice, this list of conditions and the following disclaimer. 013 * 014 * 2. Redistributions in binary form must reproduce the above copyright 015 * notice, this list of conditions and the following disclaimer in 016 * the documentation and/or other materials provided with the 017 * distribution. 018 * 019 * 3. The end-user documentation included with the redistribution, 020 * if any, must include the following acknowledgment: 021 * "This product includes software developed by the 022 * Apache Software Foundation (http://www.apache.org/)." 023 * Alternately, this acknowledgment may appear in the software itself, 024 * if and wherever such third-party acknowledgments normally appear. 025 * 026 * 4. The names "The Jakarta Project", "Commons", and "Apache Software 027 * Foundation" must not be used to endorse or promote products derived 028 * from this software without prior written permission. For written 029 * permission, please contact apache@apache.org. 030 * 031 * 5. Products derived from this software may not be called "Apache", 032 * nor may "Apache" appear in their name, without prior written 033 * permission of the Apache Software Foundation. 034 * 035 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 036 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 037 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 038 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 039 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 040 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 041 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 042 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 043 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 044 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 045 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 046 * SUCH DAMAGE. 047 * ==================================================================== 048 * 049 * This software consists of voluntary contributions made by many 050 * individuals on behalf of the Apache Software Foundation. For more 051 * information on the Apache Software Foundation, please see 052 * <http://www.apache.org/>. 053 * 054 */ 055 package org.jpu.patterns.factory; 056 057 import java.util.Iterator; 058 import java.util.List; 059 060 import org.apache.commons.lang.StringUtils; 061 import org.apache.commons.logging.Log; 062 import org.apache.commons.logging.LogFactory; 063 import org.jpu.patterns.common.JPUStringUtils; 064 import org.jpu.patterns.factory.IPrototypeFactory; 065 import org.jpu.patterns.factory.PrototypeFactoryException; 066 067 /** 068 * Implementation of {@link org.jpu.patterns.factory.IPrototypeFactory.IConfigStrategy} for property-file-based configuration. 069 * A prototype list should comply with the following syntax: 070 * <pre> 071 * <protolist> = [<protospec>{;<protospec>}*] 072 * <protospec> = [<name1>[,<name2>[,<name3>[,<nameN>]]]=<classname>] 073 * </pre> 074 * where <name1> ... <nameN> are the names under which the prototype is keyed and <classname> is the 075 * full name of the prototype implementation class, or of the {@link org.jpu.patterns.factory.IPrototypeFactory.IInstantiator} to which 076 * instantiation should be delegated. Every class listed must have an accessible no-arg constructor. 077 * <p> 078 * The source of the <protospec> is by default a System property named 079 * "<factoryClassName>.prototypes" where <factoryClassName> is the full class name of the factory 080 * being configured; but in the vast majority of cases the subclass will override the following 081 * method 082 * <pre> 083 * <code> 084 * protected String fetchPrototypeSpec(); 085 * </code> 086 * </pre> 087 * and have it return the <protospec> from whichever configuration source the application employs. 088 * <p> 089 * To specify a delimeter to use instead of ";", override the following method: 090 * <pre> 091 * <code> 092 * protected String getPrototypeDeclarationSeparator(); 093 * </code> 094 * </pre> 095 * 096 */ 097 public class PropertyFactoryConfigStrategy implements IPrototypeFactory.IConfigStrategy { 098 protected PropertyFactoryConfigStrategy(IPrototypeFactory fact) { 099 _factory = fact; 100 } 101 102 public IPrototypeFactory getFactory() { 103 return _factory; 104 } 105 106 public void reconfigureIfNecessary() {} 107 108 public void configure() { 109 boolean locked = getFactory().lock(); 110 try { 111 // Remove all prototypes first. 112 getFactory().clear(); 113 _log.debug( "Loading statically configured prototypes." ); 114 String value = getPrototypeSpec(); 115 if ( ! StringUtils.isEmpty(value) ) { 116 List protoDefs = JPUStringUtils.split(value, getPrototypeDeclarationSeparator()); 117 Iterator it = protoDefs.iterator(); 118 while ( it.hasNext() ) { 119 String protoDef = (String)it.next(); 120 processOneConfigProtoDef(protoDef); 121 } 122 } 123 } 124 finally { 125 getFactory().unlock(locked); 126 } 127 } 128 129 /** 130 * Returns whether an error is logged if the default configuration value is unavailable and the fallback is used in its place. 131 * Default is <code>true</code>, but subclasses are free to override. 132 * 133 * @return <code>true</code> if an error should be logged, <code>false</code> otherwise. 134 * 135 */ 136 protected boolean logErrorIfConfigurationUnavailable() { 137 return true; 138 } 139 140 /** 141 * Returns the prototype spec that is used if none is found in the configuration database. Default is <code>null</code>, but 142 * subclasses are free to override. 143 * 144 * @return The fallback configuration (<code>null</code> by default). 145 * 146 */ 147 protected String getFallbackPrototypeSpec() { 148 return null; 149 } 150 151 /** 152 * Returns the string that acts as a separator between <protospec>'s. Default is ";", but subclasses are free to override. 153 */ 154 protected String getPrototypeDeclarationSeparator() { 155 return ";"; 156 } 157 158 /** 159 * Returns the string that acts as a separator between successive prototype names. Default is ",", but subclasses are free to override. 160 */ 161 protected String getPrototypeNameSeparator() { 162 return ","; 163 } 164 165 /** 166 * Returns the string that acts as a separator between the list of prototype names and the name of the prototype class. 167 * Default is "=", but subclasses are free to override. 168 */ 169 protected String getAssignmentString() { 170 return "="; 171 } 172 173 /** 174 * Processes a single prototype declaration. Adds prototypes to the factory as necessary. 175 */ 176 protected void processOneConfigProtoDef(String protoDef) { 177 try { 178 if ( _log.isDebugEnabled() ) { 179 _log.debug( "Processing protoDef '" + protoDef + "'" ); 180 } 181 List keyValuePair = JPUStringUtils.split( protoDef, getAssignmentString() ); 182 if ( keyValuePair.size() == 1 ) { 183 keyValuePair.add(0, ""); 184 } 185 if ( keyValuePair.size() == 2 ) { 186 String aliases = (String)keyValuePair.get(0); 187 String className = (String)keyValuePair.get(1); 188 189 List aliasesList = JPUStringUtils.split( aliases, getPrototypeNameSeparator() ); 190 Object[] aliasesArray = aliasesList.toArray( new Object[0] ); 191 192 if ( _log.isDebugEnabled() ) { 193 _log.debug( "Trying to load class + '" + className + "'" ); 194 } 195 Object prototype = classNameToInstance(className); 196 197 if ( prototype instanceof IPrototypeFactory.IInstantiator ) { 198 IPrototypeFactory.IInstantiator instantiator = (IPrototypeFactory.IInstantiator)prototype; 199 if ( instantiator.forPrototypeOnly() ) { 200 prototype = instantiator.create( new FactoryOptions() ); 201 getFactory().initializePrototype(prototype, IPrototypeFactory.IInitializer.INSTANTIATOR, null); 202 } 203 } 204 getFactory().putPrototype(prototype, aliasesArray); 205 } 206 else { 207 throw new PrototypeFactoryException( "Invalid prototype definition '" + protoDef + "'" ); 208 } 209 } 210 catch( Throwable e ) 211 { 212 // Log as warning and move on. 213 _log.warn( "Unable to load prototype for protoDef '" + protoDef + "' for the following reason: " + e ); 214 } 215 } 216 217 /** 218 * Returns a new instance of the prototype whose name is passed. Default implementation is 219 * essentially "<code>Thread.currentThread().getContextClassLoader().loadClass(className).newInstance()</code>", but 220 * subclasses are free to override. 221 */ 222 protected Object classNameToInstance(String className) throws Throwable { 223 Class theClass = Thread.currentThread().getContextClassLoader().loadClass(className); 224 225 if ( _log.isDebugEnabled() ) { 226 _log.debug( "Trying to instantiate class + '" + className + "'" ); 227 } 228 Object prototype = theClass.newInstance(); 229 return prototype; 230 } 231 232 protected String getPrototypeSpec( ) { 233 String result = fetchPrototypeSpec(); 234 if ( result == null ) { 235 result = getFallbackPrototypeSpec(); 236 if ( logErrorIfConfigurationUnavailable() ) { 237 _log.error( "Required prototype configuration of factory class '" + getFactory().getClass().getName() + "' is unavailable. Proceeding with fallback configuration '" + result + "'" ); 238 } 239 } 240 return result; 241 } 242 243 /** 244 * This method fetches the factory's prototype list from the configuration source. The default implementation 245 * is as follows: 246 * <pre> 247 * <code> 248 * return System.getProperty( getFactory().getClass().getName() + ".prototypes", "" ); 249 * </code> 250 * </pre> 251 * However, in the majority of cases the subclass will override this to hook into whichever configuration source the application uses. 252 */ 253 protected String fetchPrototypeSpec() { 254 return System.getProperty( getFactory().getClass().getName() + ".prototypes", "" ); 255 } 256 257 private IPrototypeFactory _factory = null; 258 private static Log _log = LogFactory.getLog( PropertyFactoryConfigStrategy.class ); 259 } 260