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 Library</a> 047 * @version 2.1.0 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 ComponentStrategy strategy = 140 new ComponentStrategy( 141 partition, 142 name, 143 priority, 144 c, 145 activation, 146 lifestyle, 147 collection, 148 context, 149 parts ); 150 strategy.setElement( element ); 151 return strategy; 152 } 153 catch( IOException ioe ) 154 { 155 throw ioe; 156 } 157 catch( Exception e ) 158 { 159 final String error = 160 "Unable to construct component strategy for the class [" 161 + c.getName() 162 + "] under the partition [" 163 + partition 164 + "]"; 165 IOException ioe = new IOException( error ); 166 ioe.initCause( e ); 167 throw ioe; 168 } 169 } 170 171 /** 172 * Return the activation policy. 173 * 174 * @return the resolved activation policy 175 */ 176 private static ActivationPolicy getActivationPolicy( Element element, Profile profile, Class<?> c ) 177 { 178 if( null != element ) 179 { 180 String value = ElementHelper.getAttribute( element, "activation" ); 181 if( null != value ) 182 { 183 return ActivationPolicy.valueOf( value.toUpperCase() ); 184 } 185 } 186 if( null != profile ) 187 { 188 return profile.getActivationPolicy(); 189 } 190 if( c.isAnnotationPresent( Activation.class ) ) 191 { 192 Activation annotation = 193 c.getAnnotation( Activation.class ); 194 return annotation.value(); 195 } 196 return ActivationPolicy.SYSTEM; 197 } 198 199 /** 200 * Return the lifestyle policy. 201 * 202 * @return the resolved lifestyle policy 203 */ 204 private static LifestylePolicy getLifestylePolicy( Element element, Profile profile, Class<?> c ) 205 { 206 if( null != element ) 207 { 208 String value = ElementHelper.getAttribute( element, "lifestyle" ); 209 if( null != value ) 210 { 211 return LifestylePolicy.valueOf( value.toUpperCase() ); 212 } 213 } 214 if( null != profile ) 215 { 216 return profile.getLifestylePolicy(); 217 } 218 if( c.isAnnotationPresent( net.dpml.annotation.Component.class ) ) 219 { 220 net.dpml.annotation.Component annotation = 221 c.getAnnotation( net.dpml.annotation.Component.class ); 222 return annotation.lifestyle(); 223 } 224 return LifestylePolicy.THREAD; 225 } 226 227 /** 228 * Return the collection policy. 229 * 230 * @return the resolved collection policy 231 */ 232 private static CollectionPolicy getCollectionPolicy( Element element, Profile profile, Class<?> c ) 233 { 234 if( null != element ) 235 { 236 String value = ElementHelper.getAttribute( element, "collection" ); 237 if( null != value ) 238 { 239 return CollectionPolicy.valueOf( value.toUpperCase() ); 240 } 241 } 242 if( null != profile ) 243 { 244 return profile.getCollectionPolicy(); 245 } 246 if( c.isAnnotationPresent( net.dpml.annotation.Component.class ) ) 247 { 248 net.dpml.annotation.Component annotation = 249 c.getAnnotation( net.dpml.annotation.Component.class ); 250 return annotation.collection(); 251 } 252 return CollectionPolicy.HARD; 253 } 254 255 static int getPriority( Element element, Resolver resolver ) 256 { 257 String value = ElementHelper.getAttribute( element, "priority", null, resolver ); 258 if( null == value ) 259 { 260 return 0; 261 } 262 else 263 { 264 return Integer.parseInt( value ); 265 } 266 } 267 268 static Class loadComponentClass( ClassLoader classloader, Element element, Resolver resolver ) throws DecodingException 269 { 270 String classname = buildComponentClassname( element, resolver ); 271 try 272 { 273 return classloader.loadClass( classname ); 274 } 275 catch( ClassNotFoundException e ) 276 { 277 final String error = 278 "Class not found [" + classname + "]."; 279 Logger logger = new DefaultLogger( "dpml.lang" ); 280 String stack = StandardClassLoader.toString( classloader, null ); 281 logger.error( error + stack ); 282 throw new DecodingException( error, e, element ); 283 } 284 } 285 286 private static String buildComponentClassname( Element element, Resolver resolver ) 287 { 288 String classname = ElementHelper.getAttribute( element, "class", null, resolver ); 289 if( null != classname ) 290 { 291 return classname; 292 } 293 else 294 { 295 return ElementHelper.getAttribute( element, "type", null, resolver ); 296 } 297 } 298 299 static String getComponentName( Class c, Element element, Resolver resolver ) 300 { 301 String name = getComponentName( element, resolver ); 302 return getComponentName( c, name ); 303 } 304 305 private static String getComponentName( Element element, Resolver resolver ) 306 { 307 if( null == element ) 308 { 309 return null; 310 } 311 else 312 { 313 String key = ElementHelper.getAttribute( element, "key", null, resolver ); 314 if( null != key ) 315 { 316 return key; 317 } 318 else 319 { 320 return ElementHelper.getAttribute( element, "name", null, resolver ); 321 } 322 } 323 } 324 325 private static String getComponentName( Class<?> c ) 326 { 327 if( c.isAnnotationPresent( net.dpml.annotation.Component.class ) ) 328 { 329 net.dpml.annotation.Component annotation = 330 c.getAnnotation( net.dpml.annotation.Component.class ); 331 String name = annotation.name(); 332 if( !"".equals( name ) ) 333 { 334 return name; 335 } 336 } 337 return c.getName(); 338 } 339 340 private static String getComponentNameFromClass( Class<?> c ) 341 { 342 String classname = c.getName(); 343 int n = classname.lastIndexOf( "." ); 344 if( n > -1 ) 345 { 346 return classname.substring( n+1 ); 347 } 348 else 349 { 350 return classname; 351 } 352 } 353 354 static String getComponentPath( String partition, String name ) 355 { 356 if( null == partition ) 357 { 358 return name; 359 } 360 else 361 { 362 return partition + "." + name; 363 } 364 } 365 366 private static String getComponentName( Class c, String name ) 367 { 368 if( null != name ) 369 { 370 return name; 371 } 372 else 373 { 374 return getComponentName( c ); 375 } 376 } 377 378 static Constructor getSingleConstructor( Class c ) 379 { 380 Constructor[] constructors = c.getConstructors(); 381 if( constructors.length < 1 ) 382 { 383 final String error = 384 "The component class [" 385 + c.getName() 386 + "] does not declare a public constructor."; 387 throw new ComponentError( error ); 388 } 389 else if( constructors.length > 1 ) 390 { 391 final String error = 392 "The component class [" 393 + c.getName() 394 + "] declares more than one public constructor."; 395 throw new ComponentError( error ); 396 } 397 else 398 { 399 return constructors[0]; 400 } 401 } 402 403 private static Class getContextClass( Constructor constructor, boolean policy ) 404 { 405 final Class<?>[] types = constructor.getParameterTypes(); 406 for( Class c : types ) 407 { 408 if( ContextInvocationHandler.isaContext( c, policy ) ) 409 { 410 return c; 411 } 412 } 413 return null; 414 } 415 416 private static ContextModel getContextModel( 417 ClassLoader classloader, Class clazz, String path, 418 Profile profile, Element element, Resolver resolver, Query query, 419 boolean validate ) throws IOException 420 { 421 boolean policy = getContextHandlingPolicy( clazz ); 422 Constructor constructor = getSingleConstructor( clazz ); 423 Class subject = getContextClass( constructor, policy ); 424 ContextDirective bundled = profile.getContextDirective(); 425 ContextDirective declared = new ContextDirective( classloader, element, resolver ); 426 return new ContextModel( clazz, path, subject, policy, bundled, declared, query, validate ); 427 } 428 429 private static boolean getContextHandlingPolicy( Class c ) 430 { 431 // TODO: update to get policy as a class annotation 432 return false; // return classic evaluation policy 433 } 434 435 }