001/* 002 * jDTAUS Core API 003 * Copyright (C) 2005 Christian Schulte 004 * <cs@schulte.it> 005 * 006 * This library is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 2.1 of the License, or any later version. 010 * 011 * This library is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public 017 * License along with this library; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 019 * 020 */ 021package org.jdtaus.core.container; 022 023import java.io.ObjectStreamException; 024import java.io.Serializable; 025import java.util.Map; 026import java.util.TreeMap; 027 028/** 029 * Specification meta-data. 030 * <p>A specification consists of the properties {@code identifier}, 031 * {@code vendor}, {@code description} and {@code version}. Property 032 * {@code identifier} holds an identifier uniquely identifying the specification 033 * in a set of specifications. Property {@code vendor} holds vendor information 034 * for the vendor providing the specification. Property {@code description} 035 * holds a textual description and property {@code version} holds a textual 036 * version of the specification. The {@code stateless} flag indicates that state 037 * does not need to be retained across operations for instances to operate as 038 * specified. Property {@code multiplicity} specifies the number of 039 * implementations allowed to exist among a set of modules. A specification with 040 * {@code MULTIPLICITY_ONE} specifies that exactly one implementation of the 041 * specification must exist among a set of modules. A specification with 042 * {@code MULTIPLICITY_MANY} specifies that multiple implementations of the 043 * specification are allowed to exist among a set of modules (including none). 044 * Property {@code scope} specifies the scope the specification applies to. 045 * In multiton scope, a new instance is created whenever requested. In context 046 * scope, instances are bound to a system's context. An instance is only created 047 * if not already available in context. In singleton scope, instances are bound 048 * to a system's single instance store. An instance is only created if not 049 * already available in that single instance store.</p> 050 * 051 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 052 * @version $JDTAUS: Specification.java 8743 2012-10-07 03:06:20Z schulte $ 053 */ 054public class Specification extends ModelObject 055 implements Cloneable, Serializable 056{ 057 //--Constants--------------------------------------------------------------- 058 059 /** 060 * Constant for property {@code multiplicity}. 061 * <p>A specification with {@code MULTIPLICITY_ONE} specifies that exactly 062 * one implementation of the specification must exist among a set of 063 * modules.</p> 064 */ 065 public static final int MULTIPLICITY_ONE = 25000; 066 067 /** 068 * Constant for property {@code multiplicity}. 069 * <p>A specification with {@code MULTIPLICITY_MANY} specifies that multiple 070 * implementations of the specification are allowed to exist among a set of 071 * modules (including none).</p> 072 */ 073 public static final int MULTIPLICITY_MANY = 25001; 074 075 /** 076 * Constant for property {@code scope}. 077 * <p>In multiton scope, a new instance is created whenever requested.</p> 078 */ 079 public static final int SCOPE_MULTITON = 26000; 080 081 /** 082 * Constant for property {@code scope}. 083 * <p>In context scope, instances are bound to a system's context. An 084 * instance is only created if not already available in context.</p> 085 */ 086 public static final int SCOPE_CONTEXT = 26001; 087 088 /** 089 * Constant for property {@code scope}. 090 * <p>In singleton scope, instances are bound to a system's single instance 091 * store. An instance is only created if not already available in that 092 * single instance store.</p> 093 */ 094 public static final int SCOPE_SINGLETON = 26002; 095 096 /** Serial version UID for backwards compatibility with 1.0.x classes. */ 097 private static final long serialVersionUID = -1829249262406961967L; 098 099 //---------------------------------------------------------------Constants-- 100 //--Specification----------------------------------------------------------- 101 102 /** 103 * The name of the module holding the specification. 104 * @serial 105 */ 106 private String moduleName; 107 108 /** 109 * The description of the specification. 110 * @serial 111 * @deprecated Replaced by property {@code documentation}. 112 */ 113 private String description; 114 115 /** 116 * The identifier of the specification. 117 * @serial 118 */ 119 private String identifier; 120 121 /** 122 * The flag indicating that instances of implementations of the 123 * specification should be created using a singleton strategy. 124 * @serial 125 * @deprecated Replaced by {@link #scope}. 126 */ 127 private boolean singleton; 128 129 /** 130 * The vendor of the specification. 131 * @serial 132 */ 133 private String vendor; 134 135 /** 136 * The version of the specification. 137 * @serial 138 */ 139 private String version; 140 141 /** 142 * The implementation multiplicity of the specification. 143 * @serial 144 */ 145 private int multiplicity = MULTIPLICITY_MANY; 146 147 /** 148 * The scope the specification applies to. 149 * @serial 150 */ 151 private int scope = SCOPE_MULTITON; 152 153 /** 154 * The flag indicating if state need not be retained across method 155 * invocations for implementations to operate as specified. 156 * @serial 157 */ 158 private boolean stateless; 159 160 /** 161 * The implementations available for the specification. 162 * @serial 163 */ 164 private Implementations implementations; 165 166 /** 167 * Maps implementation names to implementations. 168 * @serial 169 */ 170 private final Map implementationNames = new TreeMap(); 171 172 /** 173 * The properties of the specification. 174 * @serial 175 */ 176 private Properties properties; 177 178 /** Creates a new {@code Specification} instance. */ 179 public Specification() 180 { 181 super(); 182 } 183 184 /** 185 * Gets the name of the module holding the specification. 186 * 187 * @return the name of the module holding the specification. 188 */ 189 public String getModuleName() 190 { 191 if ( this.moduleName == null ) 192 { 193 this.moduleName = ""; 194 } 195 196 return this.moduleName; 197 } 198 199 /** 200 * Setter for property {@code moduleName}. 201 * 202 * @param value the new name of the module holding the specification. 203 */ 204 public void setModuleName( final String value ) 205 { 206 this.moduleName = value; 207 } 208 209 /** 210 * Gets the description of the specification. 211 * 212 * @return the description of the specification or {@code null}. 213 * 214 * @deprecated Replaced by {@link #getDocumentation() getDocumentation().getValue()}. 215 */ 216 public String getDescription() 217 { 218 return this.getDocumentation().getValue(); 219 } 220 221 /** 222 * Setter for property {@code description}. 223 * 224 * @param value the new description of the specification. 225 * @deprecated Replaced by {@link #getDocumentation() getDocumentation().setValue(value)}. 226 */ 227 public void setDescription( final String value ) 228 { 229 this.getDocumentation().setValue( value ); 230 } 231 232 /** 233 * Gets the identifier of the specification. 234 * 235 * @return the unique identifier of the specification. 236 */ 237 public String getIdentifier() 238 { 239 if ( this.identifier == null ) 240 { 241 this.identifier = ""; 242 } 243 244 return this.identifier; 245 } 246 247 /** 248 * Setter for property {@code identifier}. 249 * 250 * @param value the new identifier of the specification. 251 */ 252 public void setIdentifier( final String value ) 253 { 254 this.identifier = value; 255 } 256 257 /** 258 * Gets the flag indicating the instantiation strategy of the specification. 259 * 260 * @return {@code true} if the specification is specifying a singleton; 261 * {@code false} if not. 262 * 263 * @see PropertyOverwriteConstraintException 264 * @deprecated Replaced by {@link #getScope() getScope() == SCOPE_SINGLETON}. 265 */ 266 public boolean isSingleton() 267 { 268 return this.getScope() == SCOPE_SINGLETON; 269 } 270 271 /** 272 * Setter for property {@code singleton}. 273 * 274 * @param value {@code true} to flag the specification as a singleton; 275 * {@code false} to not flag the specification as a singleton. 276 * 277 * @see PropertyOverwriteConstraintException 278 * @deprecated Replaced by {@link #setScope(int) setScope(value ? SCOPE_SINGLETON : SCOPE_MULTITON)}. 279 */ 280 public void setSingleton( final boolean value ) 281 { 282 this.scope = value ? SCOPE_SINGLETON : SCOPE_MULTITON; 283 } 284 285 /** 286 * Gets the scope the specification applies to. 287 * 288 * @return scope the specification applies to. 289 * 290 * @see #SCOPE_MULTITON 291 * @see #SCOPE_CONTEXT 292 * @see #SCOPE_SINGLETON 293 * @see PropertyOverwriteConstraintException 294 */ 295 public int getScope() 296 { 297 return this.scope; 298 } 299 300 /** 301 * Setter for property {@code scope}. 302 * 303 * @param value new scope the specification applies to. 304 * 305 * @throws IllegalArgumentException if {@code value} is not equal to one of 306 * the constants {@code SCOPE_MULTITON}, {@code SCOPE_CONTEXT} or 307 * {@code SCOPE_SINGLETON}. 308 * 309 * @see #SCOPE_MULTITON 310 * @see #SCOPE_CONTEXT 311 * @see #SCOPE_SINGLETON 312 * @see PropertyOverwriteConstraintException 313 */ 314 public void setScope( final int value ) 315 { 316 if ( value != SCOPE_MULTITON && value != SCOPE_CONTEXT && 317 value != SCOPE_SINGLETON ) 318 { 319 throw new IllegalArgumentException( Integer.toString( value ) ); 320 } 321 322 this.scope = value; 323 } 324 325 /** 326 * Gets the flag indicating if state need not be retained across method 327 * invocations for implementations to operate as specified. 328 * 329 * @return {@code true} if state need not be retained across method 330 * invocations for implementations to operate as specified; {@code false} if 331 * state must be retained across method invocations for implementations 332 * to operate as specified. 333 */ 334 public boolean isStateless() 335 { 336 return this.stateless; 337 } 338 339 /** 340 * Setter for property {@code stateless}. 341 * 342 * @param value {@code true} if state need not be retained across method 343 * invocations for implementations to operate as specified; {@code false} if 344 * state must be retained across method invocations for implementations to 345 * operate as specified. 346 */ 347 public void setStateless( final boolean value ) 348 { 349 this.stateless = value; 350 } 351 352 /** 353 * Gets the implementation multiplicity of the specification. 354 * 355 * @return one of the constants {@code MULTIPLICITY_ONE} or 356 * {@code MULTIPLICITY_MANY}. 357 * 358 * @see #MULTIPLICITY_ONE 359 * @see #MULTIPLICITY_MANY 360 * @see MultiplicityConstraintException 361 */ 362 public int getMultiplicity() 363 { 364 return this.multiplicity; 365 } 366 367 /** 368 * Setter for property {@code multiplicity}. 369 * 370 * @param value the new implementation multiplicity of the specification. 371 * 372 * @throws IllegalArgumentException if {@code value} is not equal to one of 373 * the constants {@code MULTIPLICITY_ONE} or {@code MULTIPLICITY_MANY}. 374 * @throws MultiplicityConstraintException if {@code value} equals 375 * {@code MULTIPLICITY_ONE} and the specification currently has more than 376 * one implementation defined. 377 * 378 * @see #MULTIPLICITY_ONE 379 * @see #MULTIPLICITY_MANY 380 */ 381 public void setMultiplicity( final int value ) 382 { 383 if ( value != MULTIPLICITY_ONE && value != MULTIPLICITY_MANY ) 384 { 385 throw new IllegalArgumentException( Integer.toString( value ) ); 386 } 387 if ( value == MULTIPLICITY_ONE && this.getImplementations().size() > 1 ) 388 { 389 throw new MultiplicityConstraintException( this.getIdentifier() ); 390 } 391 392 this.multiplicity = value; 393 } 394 395 /** 396 * Gets the vendor of the specification. 397 * 398 * @return the vendor of the specification. 399 */ 400 public String getVendor() 401 { 402 if ( this.vendor == null ) 403 { 404 this.vendor = ""; 405 } 406 407 return this.vendor; 408 } 409 410 /** 411 * Setter for property {@code vendor}. 412 * 413 * @param value the new vendor of the specification. 414 */ 415 public void setVendor( final String value ) 416 { 417 this.vendor = value; 418 } 419 420 /** 421 * Gets the version of the specification. 422 * 423 * @return the version of the specification or {@code null}. 424 */ 425 public String getVersion() 426 { 427 return this.version; 428 } 429 430 /** 431 * Setter for property {@code version}. 432 * 433 * @param value the new version of the specification. 434 */ 435 public void setVersion( final String value ) 436 { 437 this.version = value; 438 } 439 440 /** 441 * Gets an implementation for a name. 442 * 443 * @param name the name of the implementation to return. 444 * 445 * @return a reference to the implementation named {@code name}. 446 * 447 * @throws NullPointerException if {@code name} is {@code null}. 448 * @throws MissingImplementationException if no implementation matching 449 * {@code name} exists. 450 */ 451 public Implementation getImplementation( final String name ) 452 { 453 if ( name == null ) 454 { 455 throw new NullPointerException( "name" ); 456 } 457 458 final Implementation ret = 459 (Implementation) this.implementationNames.get( name ); 460 461 if ( ret == null ) 462 { 463 throw new MissingImplementationException( name ); 464 } 465 466 return ret; 467 } 468 469 /** 470 * Gets all available implementations of the specification. 471 * 472 * @return all available implementations of the specification. 473 */ 474 public Implementations getImplementations() 475 { 476 if ( this.implementations == null ) 477 { 478 this.implementations = new Implementations(); 479 } 480 481 return this.implementations; 482 } 483 484 /** 485 * Setter for property {@code implementations}. 486 * 487 * @param value the new implementations of the specification. 488 * 489 * @throws DuplicateImplementationException if {@code value} contains 490 * duplicate implementations. 491 * @throws MultiplicityConstraintException if the specification's 492 * multiplicity equals {@code MULTIPLICITY_ONE} and {@code value} contains 493 * no or more than one implementation. 494 */ 495 public void setImplementations( final Implementations value ) 496 { 497 if ( this.getMultiplicity() == MULTIPLICITY_ONE && value != null && 498 value.size() != 1 ) 499 { 500 throw new MultiplicityConstraintException( this.getIdentifier() ); 501 } 502 503 this.implementationNames.clear(); 504 this.implementations = null; 505 506 if ( value != null ) 507 { 508 for ( int i = value.size() - 1; i >= 0; i-- ) 509 { 510 if ( this.implementationNames.put( 511 value.getImplementation( i ).getName(), 512 value.getImplementation( i ) ) != null ) 513 { 514 this.implementationNames.clear(); 515 516 throw new DuplicateImplementationException( 517 value.getImplementation( i ).getName() ); 518 519 } 520 } 521 522 this.implementations = value; 523 } 524 } 525 526 /** 527 * Gets the properties of the specification. 528 * 529 * @return the properties of the specification. 530 */ 531 public Properties getProperties() 532 { 533 if ( this.properties == null ) 534 { 535 this.properties = new Properties(); 536 } 537 538 return this.properties; 539 } 540 541 /** 542 * Setter for property {@code properties}. 543 * 544 * @param value new properties of the specification. 545 */ 546 public void setProperties( final Properties value ) 547 { 548 this.properties = value; 549 } 550 551 /** 552 * Creates a string representing the properties of the instance. 553 * 554 * @return a string representing the properties of the instance. 555 */ 556 private String internalString() 557 { 558 final StringBuffer buf = new StringBuffer( 500 ).append( '{' ). 559 append( this.internalString( this ) ). 560 append( ", identifier=" ).append( this.identifier ). 561 append( ", moduleName=" ).append( this.moduleName ). 562 append( ", stateless=" ).append( this.stateless ). 563 append( ", multiplicity=" ). 564 append( this.multiplicity == MULTIPLICITY_ONE ? "one" : "many" ). 565 append( ", scope=" ).append( this.scope == SCOPE_MULTITON 566 ? "multiton" 567 : this.scope == SCOPE_CONTEXT 568 ? "context" 569 : "singleton" ). 570 append( ", vendor=" ).append( this.vendor ). 571 append( ", version=" ).append( this.version ). 572 append( ", properties=" ).append( this.getProperties() ). 573 append( ", implementations={" ); 574 575 for ( int i = this.getImplementations().size() - 1; i >= 0; i-- ) 576 { 577 final Implementation impl = 578 this.getImplementations().getImplementation( i ); 579 580 buf.append( "[" ).append( i ).append( "]=" ). 581 append( impl.getIdentifier() ).append( "@" ). 582 append( impl.getVersion() ); 583 584 if ( i - 1 >= 0 ) 585 { 586 buf.append( ", " ); 587 } 588 } 589 590 buf.append( "}}" ); 591 return buf.toString(); 592 } 593 594 //-----------------------------------------------------------Specification-- 595 //--Serializable------------------------------------------------------------ 596 597 /** 598 * Takes care of initializing fields when constructed from an 1.0.x object 599 * stream. 600 * 601 * @throws ObjectStreamException if no scope can be resolved. 602 */ 603 private Object readResolve() throws ObjectStreamException 604 { 605 if ( this.scope == SCOPE_MULTITON && this.singleton ) 606 { 607 this.scope = SCOPE_SINGLETON; 608 } 609 if ( this.getDocumentation().getValue() == null && 610 this.description != null ) 611 { 612 this.getDocumentation().setValue( this.description ); 613 } 614 615 return this; 616 } 617 618 //------------------------------------------------------------Serializable-- 619 //--Object------------------------------------------------------------------ 620 621 /** 622 * Returns a string representation of the object. 623 * 624 * @return a string representation of the object. 625 */ 626 public String toString() 627 { 628 return super.toString() + this.internalString(); 629 } 630 631 /** 632 * Creates and returns a copy of this object. This method performs a 633 * "shallow copy" of this object, not a "deep copy" operation. 634 * 635 * @return a clone of this instance. 636 */ 637 public Object clone() 638 { 639 try 640 { 641 return super.clone(); 642 } 643 catch ( final CloneNotSupportedException e ) 644 { 645 throw new AssertionError( e ); 646 } 647 } 648 649 /** 650 * Indicates whether some other object is equal to this one by comparing 651 * properties {@code identifier} and {@code version}. 652 * 653 * @param o the reference object with which to compare. 654 * 655 * @return {@code true} if this object is the same as {@code o}; 656 * {@code false} otherwise. 657 */ 658 public final boolean equals( final Object o ) 659 { 660 boolean equal = o == this; 661 if ( !equal && o instanceof Specification ) 662 { 663 final Specification that = (Specification) o; 664 equal = this.getIdentifier().equals( that.getIdentifier() ) && 665 ( this.getVersion() == null ? that.getVersion() == null 666 : this.getVersion().equals( that.getVersion() ) ); 667 668 } 669 670 return equal; 671 } 672 673 /** 674 * Returns a hash code value for this object. 675 * 676 * @return a hash code value for this object. 677 */ 678 public final int hashCode() 679 { 680 return this.getIdentifier().hashCode() + 681 ( this.getVersion() == null ? 0 : this.getVersion().hashCode() ); 682 683 } 684 685 //------------------------------------------------------------------Object-- 686}