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