001 /* 002 * Copyright 2004-2006 Stephen J. McConnell. 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 013 * implied. 014 * 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019 package net.dpml.runtime; 020 021 import dpml.lang.Classpath; 022 import dpml.lang.DOM3DocumentBuilder; 023 import dpml.lang.Value; 024 import dpml.util.StandardClassLoader; 025 import dpml.util.ElementHelper; 026 import dpml.util.DefaultLogger; 027 028 import java.io.IOException; 029 import java.io.Writer; 030 import java.net.URI; 031 import java.net.URL; 032 import java.util.Map; 033 import java.lang.reflect.Constructor; 034 035 import net.dpml.lang.DecodingException; 036 import net.dpml.lang.Strategy; 037 import net.dpml.lang.StrategyHandler; 038 import net.dpml.lang.Buffer; 039 040 import net.dpml.annotation.Activation; 041 import net.dpml.annotation.ActivationPolicy; 042 import net.dpml.annotation.LifestylePolicy; 043 import net.dpml.annotation.CollectionPolicy; 044 045 import net.dpml.util.Resolver; 046 import net.dpml.util.Logger; 047 048 import org.w3c.dom.Document; 049 import org.w3c.dom.Element; 050 051 /** 052 * Strategy handler for the component model. 053 * 054 * @author <a href="http://www.dpml.net">Digital Product Management Laboratory</a> 055 * @version 2.0.0 056 */ 057 public class ComponentStrategyHandler implements StrategyHandler 058 { 059 public static final String NAMESPACE = "dpml:metro"; 060 061 private static final DOM3DocumentBuilder BUILDER = new DOM3DocumentBuilder(); 062 063 public Strategy newStrategy( Class<?> c ) throws IOException 064 { 065 return newStrategy( c, null ); 066 } 067 068 public Strategy newStrategy( Class<?> c, String name ) throws IOException 069 { 070 String spec = getComponentName( c, name ); 071 Profile profile = new Profile( c, spec, null ); 072 ContextModel context = getContextModel( null, c, spec, profile, null, null, null, true ); 073 PartsDirective parts = profile.getPartsDirective(); 074 return new ComponentStrategy( 075 null, spec, 0, c, ActivationPolicy.SYSTEM, LifestylePolicy.THREAD, 076 CollectionPolicy.HARD, context, parts ); 077 } 078 079 public Component newComponent( Class<?> c, String name ) throws IOException 080 { 081 return (Component) newStrategy( c, name ); 082 } 083 084 /** 085 * Construct a strategy definition using a supplied element and value resolver. 086 * @param classloader the operational classloader 087 * @param element the DOM element definining the deployment strategy 088 * @param resolver symbolic property resolver 089 * @param partition the enclosing partition 090 * @param query the query fragment to applied to the component context definition 091 * @return the strategy definition 092 * @exception IOException if an I/O error occurs 093 */ 094 public Strategy build( 095 ClassLoader classloader, Element element, Resolver resolver, String partition, 096 String query, boolean validate ) throws IOException 097 { 098 Class c = loadComponentClass( classloader, element, resolver ); 099 String name = getComponentName( c, element, resolver ); 100 int priority = getPriority( element, resolver ); 101 String path = getComponentPath( partition, name ); 102 Profile profile = new Profile( c, path, resolver ); 103 104 Query q = new Query( query ); 105 Element contextElement = ElementHelper.getChild( element, "context" ); 106 ContextModel context = 107 getContextModel( 108 classloader, c, path, profile, contextElement, resolver, q, validate ); 109 ActivationPolicy activation = getActivationPolicy( element, profile, c ); 110 LifestylePolicy lifestyle = getLifestylePolicy( element, profile, c ); 111 CollectionPolicy collection = getCollectionPolicy( element, profile, c ); 112 113 try 114 { 115 Element partsElement = ElementHelper.getChild( element, "parts" ); 116 PartsDirective parts = 117 new PartsDirective( 118 profile.getPartsDirective(), 119 classloader, 120 partsElement, 121 resolver, 122 path ); 123 124 ComponentStrategy strategy = 125 new ComponentStrategy( 126 partition, 127 name, 128 priority, 129 c, 130 activation, 131 lifestyle, 132 collection, 133 context, 134 parts ); 135 136 return strategy; 137 } 138 catch( IOException ioe ) 139 { 140 throw ioe; 141 } 142 catch( Exception e ) 143 { 144 final String error = 145 "Unable to construct component strategy for the class [" 146 + c.getName() 147 + "] under the partition [" 148 + partition 149 + "]"; 150 IOException ioe = new IOException( error ); 151 ioe.initCause( e ); 152 throw ioe; 153 } 154 } 155 156 /** 157 * Return the activation policy. 158 * 159 * @return the resolved activation policy 160 */ 161 private static ActivationPolicy getActivationPolicy( Element element, Profile profile, Class<?> c ) 162 { 163 if( null != element ) 164 { 165 String value = ElementHelper.getAttribute( element, "activation" ); 166 if( null != value ) 167 { 168 return ActivationPolicy.valueOf( value.toUpperCase() ); 169 } 170 } 171 if( null != profile ) 172 { 173 return profile.getActivationPolicy(); 174 } 175 if( c.isAnnotationPresent( Activation.class ) ) 176 { 177 Activation annotation = 178 c.getAnnotation( Activation.class ); 179 return annotation.value(); 180 } 181 return ActivationPolicy.SYSTEM; 182 } 183 184 /** 185 * Return the lifestyle policy. 186 * 187 * @return the resolved lifestyle policy 188 */ 189 private static LifestylePolicy getLifestylePolicy( Element element, Profile profile, Class<?> c ) 190 { 191 if( null != element ) 192 { 193 String value = ElementHelper.getAttribute( element, "lifestyle" ); 194 if( null != value ) 195 { 196 return LifestylePolicy.valueOf( value.toUpperCase() ); 197 } 198 } 199 if( null != profile ) 200 { 201 return profile.getLifestylePolicy(); 202 } 203 if( c.isAnnotationPresent( net.dpml.annotation.Component.class ) ) 204 { 205 net.dpml.annotation.Component annotation = 206 c.getAnnotation( net.dpml.annotation.Component.class ); 207 return annotation.lifestyle(); 208 } 209 return LifestylePolicy.THREAD; 210 } 211 212 /** 213 * Return the collection policy. 214 * 215 * @return the resolved collection policy 216 */ 217 private static CollectionPolicy getCollectionPolicy( Element element, Profile profile, Class<?> c ) 218 { 219 if( null != element ) 220 { 221 String value = ElementHelper.getAttribute( element, "collection" ); 222 if( null != value ) 223 { 224 return CollectionPolicy.valueOf( value.toUpperCase() ); 225 } 226 } 227 if( null != profile ) 228 { 229 return profile.getCollectionPolicy(); 230 } 231 if( c.isAnnotationPresent( net.dpml.annotation.Component.class ) ) 232 { 233 net.dpml.annotation.Component annotation = 234 c.getAnnotation( net.dpml.annotation.Component.class ); 235 return annotation.collection(); 236 } 237 return CollectionPolicy.HARD; 238 } 239 240 private int getPriority( Element element, Resolver resolver ) 241 { 242 String value = ElementHelper.getAttribute( element, "priority", null, resolver ); 243 if( null == value ) 244 { 245 return 0; 246 } 247 else 248 { 249 return Integer.parseInt( value ); 250 } 251 } 252 253 private Class loadComponentClass( ClassLoader classloader, Element element, Resolver resolver ) throws DecodingException 254 { 255 String classname = buildComponentClassname( element, resolver ); 256 try 257 { 258 return classloader.loadClass( classname ); 259 } 260 catch( ClassNotFoundException e ) 261 { 262 final String error = 263 "Class not found [" + classname + "]."; 264 Logger logger = new DefaultLogger( "dpml.lang" ); 265 String stack = StandardClassLoader.toString( classloader, null ); 266 logger.error( error + stack ); 267 throw new DecodingException( error, e, element ); 268 } 269 } 270 271 private String buildComponentClassname( Element element, Resolver resolver ) 272 { 273 String classname = ElementHelper.getAttribute( element, "class", null, resolver ); 274 if( null != classname ) 275 { 276 return classname; 277 } 278 else 279 { 280 return ElementHelper.getAttribute( element, "type", null, resolver ); 281 } 282 } 283 284 private static String getComponentName( Class c, Element element, Resolver resolver ) 285 { 286 String name = getComponentName( element, resolver ); 287 return getComponentName( c, name ); 288 } 289 290 private static String getComponentName( Element element, Resolver resolver ) 291 { 292 if( null == element ) 293 { 294 return null; 295 } 296 else 297 { 298 String key = ElementHelper.getAttribute( element, "key", null, resolver ); 299 if( null != key ) 300 { 301 return key; 302 } 303 else 304 { 305 return ElementHelper.getAttribute( element, "name", null, resolver ); 306 } 307 } 308 } 309 310 private static String getComponentName( Class<?> c ) 311 { 312 if( c.isAnnotationPresent( net.dpml.annotation.Component.class ) ) 313 { 314 net.dpml.annotation.Component annotation = 315 c.getAnnotation( net.dpml.annotation.Component.class ); 316 String name = annotation.name(); 317 if( !"".equals( name ) ) 318 { 319 return name; 320 } 321 } 322 return c.getName(); 323 } 324 325 private static String getComponentNameFromClass( Class<?> c ) 326 { 327 String classname = c.getName(); 328 int n = classname.lastIndexOf( "." ); 329 if( n > -1 ) 330 { 331 return classname.substring( n+1 ); 332 } 333 else 334 { 335 return classname; 336 } 337 } 338 339 private static String getComponentPath( String partition, String name ) 340 { 341 if( null == partition ) 342 { 343 return name; 344 } 345 else 346 { 347 return partition + "." + name; 348 } 349 } 350 351 private static String getComponentName( Class c, String name ) 352 { 353 if( null != name ) 354 { 355 return name; 356 } 357 else 358 { 359 return getComponentName( c ); 360 } 361 } 362 363 static Constructor getSingleConstructor( Class c ) 364 { 365 Constructor[] constructors = c.getConstructors(); 366 if( constructors.length < 1 ) 367 { 368 final String error = 369 "The component class [" 370 + c.getName() 371 + "] does not declare a public constructor."; 372 throw new ComponentError( error ); 373 } 374 else if( constructors.length > 1 ) 375 { 376 final String error = 377 "The component class [" 378 + c.getName() 379 + "] declares more than one public constructor."; 380 throw new ComponentError( error ); 381 } 382 else 383 { 384 return constructors[0]; 385 } 386 } 387 388 private static Class getContextClass( Constructor constructor, boolean policy ) 389 { 390 final Class<?>[] types = constructor.getParameterTypes(); 391 for( Class c : types ) 392 { 393 if( ContextInvocationHandler.isaContext( c, policy ) ) 394 { 395 return c; 396 } 397 } 398 return null; 399 } 400 401 private static ContextModel getContextModel( 402 ClassLoader classloader, Class clazz, String path, 403 Profile profile, Element element, Resolver resolver, Query query, 404 boolean validate ) throws IOException 405 { 406 boolean policy = getContextHandlingPolicy( clazz ); 407 Constructor constructor = getSingleConstructor( clazz ); 408 Class subject = getContextClass( constructor, policy ); 409 ContextDirective bundled = profile.getContextDirective(); 410 ContextDirective declared = new ContextDirective( classloader, element, resolver ); 411 return new ContextModel( clazz, path, subject, policy, bundled, declared, query, validate ); 412 } 413 414 private static boolean getContextHandlingPolicy( Class c ) 415 { 416 // TODO: update to get policy as a class annotation 417 return false; // return classic evaluation policy 418 } 419 420 }