001 /* 002 Copyright (C) 2001-2003 Renaud Pawlak <renaud@aopsys.com> 003 004 This program is free software; you can redistribute it and/or modify 005 it under the terms of the GNU Lesser General Public License as 006 published by the Free Software Foundation; either version 2 of the 007 License, or (at your option) any later version. 008 009 This program is distributed in the hope that it will be useful, 010 but WITHOUT ANY WARRANTY; without even the implied warranty of 011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 GNU Lesser General Public License for more details. 013 014 You should have received a copy of the GNU Lesser General Public 015 License along with this program; if not, write to the Free Software 016 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 017 USA */ 018 019 package org.objectweb.jac.core; 020 021 022 import java.io.Serializable; 023 import java.lang.reflect.Array; 024 import java.net.URL; 025 import org.apache.log4j.Logger; 026 import org.objectweb.jac.core.rtti.*; 027 import org.objectweb.jac.util.Classes; 028 import org.objectweb.jac.util.Strings; 029 030 /** 031 * This class defines aspect component configurations so that the 032 * programmer or the system administrator will be able to configure 033 * the available aspects for a given application. 034 * 035 * <p>The default available aspects in the system are the ones that 036 * are declared to the AC manager in the <code>jac.prop</code> (see 037 * the <code>org.objectweb.jac.acs</code> property). 038 * 039 * @see ApplicationRepository 040 * @see Application 041 * @see Parser 042 * @see ACManager 043 */ 044 045 public class ACConfiguration implements Serializable { 046 static Logger logger = Logger.getLogger("jac"); 047 static Logger loggerAspects = Logger.getLogger("aspects"); 048 static Logger loggerConf = Logger.getLogger("aspects.config"); 049 static Logger loggerPerf = Logger.getLogger("perf"); 050 051 /** The application this configuration belongs to. */ 052 protected Application application; 053 /** The name of the configured AC. */ 054 protected String name; 055 /** The aspect component that corresponds to this configuration 056 once instantiated (null before). This is a local aspect 057 component. */ 058 transient AspectComponent instance = null; 059 060 /** The configuration file's URL. */ 061 protected URL filePath; 062 063 String acPath = null; 064 065 /** 066 * Sets the URL of the configuration file that defines the 067 * configuration operations. 068 * 069 * @param filePath a valid file path 070 * @see #getURL() */ 071 072 public void setURL(URL filePath) { 073 this.filePath = filePath; 074 } 075 076 /** 077 * The getter of the configuration file's URL. 078 * 079 * @return the URL 080 * @see #setURL(URL) */ 081 082 public URL getURL() { 083 return filePath; 084 } 085 086 /** 087 * This flag tells if the aspect that is configured by the current 088 * configuration will be woven on demand (by the administrator or 089 * by a configuration program) or if the aspect will be 090 * automatically woven and restored by the system. 091 * 092 * <p>For instance, a persistence aspect should always have this 093 * configuration flag to false whilst a debugging aspect should 094 * most of the time be woven on demand (when debugging is 095 * needed). */ 096 097 protected boolean weaveOnDemand = true; 098 099 /** 100 * Gets the <code>weaveOnDemand</code> flag value. 101 * @return the flag value */ 102 103 public boolean getWeaveOnDemand() { 104 return weaveOnDemand; 105 } 106 107 /** 108 * Sets the <code>weaveOnDemand</code> flag value. 109 * @param b the new flag value */ 110 111 public void setWeaveOnDemand(boolean b) { 112 weaveOnDemand = b; 113 } 114 115 /** 116 * Creates a new aspect component configuration. 117 * 118 * @param application the application this configuration belongs to 119 * @param name the name of the AC as defined in the declared ACs of 120 * the AC manager, or the of the aspect component's class 121 * @param filePath the path of the configuration file; it can be 122 * absolute but, if relative, it is automatically concatened to the 123 * application's path 124 * @param weaveNow a true value means that the aspect that 125 * configured by this configuration will be automatically woven at 126 * the application's start, a false value means that the user will 127 * have to weave it with a program (or with the administration 128 * GUI); default is true */ 129 130 public ACConfiguration(Application application, String name, 131 String filePath, boolean weaveNow) { 132 this.name = name; 133 this.application = application; 134 this.weaveOnDemand = ! weaveNow; 135 if (filePath!=null) { 136 try { 137 if (filePath.startsWith("file:")) { 138 filePath = filePath.substring(5); 139 } 140 this.filePath = new URL("file:"+filePath); 141 } catch (Exception e) { 142 e.printStackTrace(); 143 } 144 } 145 146 // If name is not the name of a registered AC, 147 // It must be the class name of the AC 148 ACManager acm = ACManager.getACM(); 149 if (!acm.isACDeclared(name)) { 150 acm.declareAC(name,name); 151 } 152 } 153 154 /** 155 * Gets the name of the configured AC as defined in the declared 156 * ACs of the AC manager. 157 * 158 * @return the AC name 159 * @see #setName(String) 160 * @see ACManager */ 161 162 public String getName() { 163 return name; 164 } 165 166 /** 167 * The aspect name setter. Must be declared in the 168 * <code>jac.prop</code>. 169 * 170 * @param name the aspect name 171 * @see ACManager */ 172 173 public void setName(String name) { 174 this.name = name; 175 } 176 177 /** 178 * Gets the aspect component instance that corresponds to this 179 * configuration. 180 * 181 * @return the AC instance */ 182 183 public AspectComponent getInstance() { 184 return instance; 185 } 186 187 /** 188 * Gets the owning application. 189 * 190 * @return the application that owns this configuration */ 191 192 public Application getApplication() { 193 return application; 194 } 195 196 /** 197 * Return the class item for the configured aspect component. 198 * 199 * @return the AC type */ 200 201 public ClassItem getAspectClass() { 202 String acPath = ((ACManager)ACManager.get()).getACPathFromName(name); 203 return ClassRepository.get().getClass(acPath); 204 } 205 206 /** 207 * Instantiates a new aspect component. 208 * 209 * @return the new aspect component, null if something went wrong 210 */ 211 212 protected AspectComponent instantiate() { 213 loggerAspects.debug("instantiating "+name); 214 ACManager acm = (ACManager)ACManager.get(); 215 // String oldAC = Collaboration.get().getCurAC(); 216 // Collaboration.get().setCurAC( 217 // getApplication().getName() + "." + name ); 218 try { 219 instance = (AspectComponent) acm.getObject( 220 application.getName()+"."+name); 221 if (instance == null) { 222 if (acPath == null) 223 acPath = acm.getACPathFromName(name); 224 loggerAspects.debug(name + " : " + acPath); 225 if( acPath == null ) return null; 226 Class cl = Class.forName( acPath ); 227 loggerAspects.debug("instantiating "+cl); 228 instance = (AspectComponent) cl.newInstance(); 229 instance.setApplication(getApplication().getName()); 230 } 231 return instance; 232 } catch(Exception e) { 233 e.printStackTrace(); 234 return null; 235 } finally { 236 // Collaboration.get().setCurAC(oldAC); 237 } 238 } 239 240 /** 241 * Configures the aspect component. 242 * 243 * <p>This method takes the aspect component instance that corresponds 244 * to this configuration, parse the configuration file, and calls 245 * all the configuration operations that are defined in this file. 246 * 247 * @see Parser 248 * @see #getInstance() */ 249 250 251 protected void configure() { 252 logger.info("--- configuring "+name+" aspect ---"); 253 long start = System.currentTimeMillis(); 254 String[] defaults = instance.getDefaultConfigs(); 255 for (int i=0; i<defaults.length; i++) { 256 instance.configure(name,defaults[i]); 257 } 258 instance.configure(name,filePath.getFile()); 259 instance.whenConfigured(); 260 loggerPerf.info("aspect "+name+" configured in "+ 261 (System.currentTimeMillis()-start)+"ms"); 262 } 263 264 public static Object convertArray(Object[] array, Class componentType, Imports imports) 265 throws Exception 266 { 267 Object result = Array.newInstance(componentType, array.length); 268 for (int i=0; i<array.length; i++) { 269 Array.set(result,i,convertValue(array[i],componentType,imports)); 270 } 271 return result; 272 } 273 274 public static Object convertValue(Object object, Class type) 275 throws Exception 276 { 277 return convertValue(object,type,null); 278 } 279 280 public static Object convertValue(Object object, Class type, Imports imports) 281 throws Exception 282 { 283 Object result = object; 284 if (object != null && object.getClass() != type) { 285 try { 286 if (type.isArray()) { 287 result = convertArray((Object[])object,type.getComponentType(),imports); 288 } else if (type==double.class || type==Double.class) { 289 result = new Double((String)object); 290 } else if (type==int.class || type==Integer.class) { 291 result = new Integer((String)object); 292 } else if (type==long.class || type==Long.class) { 293 result = new Long((String)object); 294 } else if (type==float.class || type==Float.class) { 295 result = new Float((String)object); 296 } else if (type==boolean.class || type==Boolean.class) { 297 result = Boolean.valueOf((String)object); 298 } else if (type==short.class || type==Short.class) { 299 result = new Short((String)object); 300 } else if (type==Class.class) { 301 result = Class.forName((String)object); 302 } else if (type==ClassItem.class) { 303 result = getClass((String)object,imports); 304 } else if (type==VirtualClassItem.class) { 305 result = ClassRepository.get().getVirtualClassStrict((String)object); 306 } else if (AbstractMethodItem.class.isAssignableFrom(type)) { 307 String str = (String)object; 308 int index = str.indexOf("("); 309 try { 310 ClassItem classItem; 311 if (index==-1) { 312 classItem = getClass(str,imports); 313 result = classItem.getConstructor(""); 314 } else { 315 classItem = getClass( 316 str.substring(0,index),imports); 317 318 // resolve parameter types 319 String[] paramTypes = 320 Strings.split(str.substring(index+1,str.length()-1), ","); 321 resolveTypes(paramTypes,imports); 322 String fullName = str.substring(0,index+1)+Strings.join(paramTypes,",")+")"; 323 result = classItem.getConstructor(fullName.substring(index)); 324 } 325 } catch (NoSuchClassException e) { 326 if (index!=-1) { 327 // resolve parameter types 328 String[] paramTypes = 329 Strings.split(str.substring(index+1,str.length()-1), ","); 330 resolveTypes(paramTypes,imports); 331 str = str.substring(0,index+1)+Strings.join(paramTypes,",")+")"; 332 index = str.lastIndexOf(".",index); 333 } else { 334 index = str.lastIndexOf("."); 335 } 336 if (index!=-1) { 337 try { 338 ClassItem classItem = getClass( 339 str.substring(0,index),imports); 340 result = classItem.getAbstractMethod( 341 str.substring(index+1)); 342 } catch (NoSuchClassException e2) { 343 throw new Exception("Failed to convert "+str+ 344 " into a "+type.getName()); 345 } 346 } else { 347 throw new Exception("Failed to convert "+str+ 348 " into a "+type.getName()); 349 } 350 } 351 } else if (FieldItem.class.isAssignableFrom(type)) { 352 String str = (String)object; 353 loggerConf.debug("Trying to convert "+str+" into a FieldItem"); 354 int index = str.length(); 355 result = null; 356 while (index!=-1 && result==null) { 357 index = str.lastIndexOf(".",index-1); 358 if (index!=-1) { 359 try { 360 loggerConf.debug( 361 " Trying class="+str.substring(0,index)+ 362 " and field="+str.substring(index+1)); 363 ClassItem classItem = getClass( 364 str.substring(0,index),imports); 365 result = classItem.getField(str.substring(index+1)); 366 loggerConf.debug(" -> "+result); 367 } catch (NoSuchClassException e) { 368 loggerConf.info( 369 " Failed conversion of "+object+ 370 " to FieldItem with class="+str.substring(0,index)+ 371 " and field="+str.substring(index+1)); 372 } 373 } 374 } 375 if (index==-1 || result==null) { 376 throw new Exception("Failed to convert "+str+ 377 " into a "+type.getName()); 378 } 379 } else if (MemberItem.class.isAssignableFrom(type)) { 380 String str = (String)object; 381 int index = -1; 382 int paren = str.indexOf("("); 383 if (paren==-1) 384 index = str.length(); 385 else 386 index = paren; 387 388 result = null; 389 while (index!=-1 && result==null) { 390 index = str.lastIndexOf(".",index-1); 391 if (index!=-1) { 392 try { 393 ClassItem classItem = getClass( 394 str.substring(0,index),imports); 395 result = classItem.getMember(str.substring(index+1)); 396 } catch (NoSuchClassException e) { 397 } 398 } 399 } 400 401 if (index==-1 || result==null) { 402 throw new Exception("Failed to convert "+str+ 403 " into a "+type.getName()); 404 } 405 } else { 406 throw new Exception("Don't know how to convert "+object+" into a "+type); 407 } 408 } catch (Exception e) { 409 loggerConf.info("Failed to convert "+object+" into "+type.getName(),e); 410 throw new Exception( 411 "Failed to convert "+object+" into "+type.getName()+" : "+e); 412 } 413 } 414 loggerConf.debug("Converted "+object+" into "+type+": "+result); 415 return result; 416 } 417 418 protected static void resolveTypes(String[] types, Imports imports) { 419 for (int i=0; i<types.length; i++) { 420 String type = types[i]; 421 boolean isArray = false; 422 if (type.endsWith("[]")) { 423 type = type.substring(0,type.length()-2); 424 } 425 try { 426 if (isArray) 427 types[i] = imports.getClass(types[i].substring(0,types[i].length()-2)).getName()+"[]"; 428 else 429 types[i] = imports.getClass(types[i]).getName(); 430 } catch (NoSuchClassException nse) { 431 if (!Classes.isPrimitiveType(type)) 432 throw nse; 433 } 434 } 435 } 436 437 protected static ClassItem getClass(String name, Imports imports) { 438 if (imports!=null) 439 return imports.getClass(name); 440 else 441 return ClassRepository.get().getClass(name); 442 } 443 444 /** 445 * Instantiates, configures, and weaves the aspect component that 446 * corresponds to this configuration. 447 * 448 * @see #instantiate() 449 * @see #configure() 450 */ 451 public void weave() { 452 //acs.put( acName, ac ); 453 instantiate(); 454 if (instance == null) { 455 logger.error("could not instantiate aspect "+name); 456 return; 457 } 458 try { 459 instance.beforeConfiguration(); 460 configure(); 461 } catch (Exception e) { 462 e.printStackTrace(); 463 return; 464 } 465 loggerAspects.debug( 466 "registering " + instance + 467 " on application " + getApplication() ); 468 // This line is useful for composite aspects 469 instance.doRegister(); 470 ACManager.get().register( getApplication().getName() + 471 "." + name, instance ); 472 } 473 474 /** 475 * Unweaves the aspect component that corresponds to this 476 * configuration. 477 */ 478 public void unweave() { 479 // This line is useful for composite aspects 480 instance.doUnregister(); 481 ((ACManager)ACManager.get()).unregister( application.getName() + "." + name ); 482 instance = null; 483 } 484 485 /** 486 * Returns a string representation of this configuration. 487 * @return a string 488 */ 489 public String toString() { 490 return "AC " + name + " configuration"; 491 } 492 493 }