001 /* 002 * Copyright 2004 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.metro.info; 020 021 import java.util.Properties; 022 023 import net.dpml.lang.Version; 024 025 /** 026 * This class is used to provide explicit information to assembler 027 * and administrator about the Component. It includes information 028 * such as; 029 * 030 * <ul> 031 * <li>a symbolic name</li> 032 * <li>classname</li> 033 * <li>version</li> 034 * </ul> 035 * 036 * <p>The InfoDescriptor also includes an arbitrary set 037 * of attributes about component. Usually these are container 038 * specific attributes that can store arbitrary information. 039 * The attributes should be stored with keys based on package 040 * name of container. 041 * 042 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a> 043 * @version 1.0.4 044 */ 045 public final class InfoDescriptor extends Descriptor 046 { 047 //------------------------------------------------------------------- 048 // static 049 //------------------------------------------------------------------- 050 051 /** 052 * Serial version identifier. 053 */ 054 static final long serialVersionUID = 1L; 055 056 //------------------------------------------------------------------- 057 // immutable state 058 //------------------------------------------------------------------- 059 060 /** 061 * The short name of the Component Type. Useful for displaying 062 * human readable strings describing the type in 063 * assembly tools or generators. 064 */ 065 private final String m_name; 066 067 /** 068 * The implementation classname. 069 */ 070 private final String m_classname; 071 072 /** 073 * The version of component that descriptor describes. 074 */ 075 private final Version m_version; 076 077 /** 078 * The component lifestyle. 079 */ 080 private final LifestylePolicy m_lifestyle; 081 082 /** 083 * The component garbage collection policy. The value returned is either 084 * WEAK, SOFT, HARD or HARD. A component implementing a WEAK policy 085 * will be decommissioned if no references exist. A component declaring a 086 * SOFT policy will exist without reference so long as memory contention 087 * does not occur. A component implementing HARD policies will be 088 * maintained irrespective of usage and memory constraints. The default 089 * policy is SYSTEM which implies delegation of policy selection to the 090 * component's container. 091 */ 092 private final CollectionPolicy m_collection; 093 094 /** 095 * Threadsafe policy. 096 */ 097 private final ThreadSafePolicy m_threadsafe; 098 099 100 //------------------------------------------------------------------- 101 // constructor 102 //------------------------------------------------------------------- 103 104 /** 105 * Creation of a new info descriptor using a supplied name, key, version 106 * and attribute set. 107 * 108 * @param name the default component name 109 * @param classname the implemetation classname 110 * @exception IllegalArgumentException if the classname is invalid 111 * @exception NullPointerException if the classname is null 112 */ 113 public InfoDescriptor( final String name, final String classname ) 114 throws IllegalArgumentException, NullPointerException 115 { 116 this( name, classname, null, null, CollectionPolicy.SYSTEM, ThreadSafePolicy.UNKNOWN, null ); 117 } 118 119 /** 120 * Creation of a new info descriptor using a supplied name, key, version 121 * and attribute set. 122 * 123 * @param name the default component name 124 * @param classname the implemetation classname 125 * @param version the implementation version 126 * @param lifestyle the component lifestyle (singleton, thread, etc.) 127 * @param collection the garbage collection policy for the component 128 * @param threadsafe if TRUE the type is declaring itself as threadsafe 129 * @param attributes a set of attributes associated with the component type 130 * @exception IllegalArgumentException if the implementation classname is invalid 131 * @exception NullPointerException if the classname argument is null. 132 */ 133 public InfoDescriptor( final String name, 134 final String classname, 135 final Version version, 136 final LifestylePolicy lifestyle, 137 final CollectionPolicy collection, 138 final ThreadSafePolicy threadsafe, 139 final Properties attributes ) 140 throws IllegalArgumentException, NullPointerException 141 { 142 super( attributes ); 143 144 m_threadsafe = threadsafe; 145 146 if( null == classname ) 147 { 148 throw new NullPointerException( "classname" ); 149 } 150 151 if( classname.indexOf( "/" ) > -1 ) 152 { 153 throw new IllegalArgumentException( "classname: " + classname ); 154 } 155 156 m_classname = classname; 157 158 if( null == version ) 159 { 160 m_version = Version.parse( "0" ); 161 } 162 else 163 { 164 m_version = version; 165 } 166 167 if( lifestyle == null ) 168 { 169 m_lifestyle = LifestylePolicy.SYSTEM; 170 } 171 else 172 { 173 m_lifestyle = lifestyle; 174 } 175 176 if( null == collection ) 177 { 178 m_collection = CollectionPolicy.SYSTEM; 179 } 180 else 181 { 182 m_collection = collection; 183 } 184 185 if( name != null ) 186 { 187 m_name = name; 188 } 189 else 190 { 191 m_name = getClassName( classname ); 192 } 193 } 194 195 /** 196 * Internal utility to get the name of the class without the package name. Used 197 * when constructing a default component name. 198 * @param classname the fully qualified classname 199 * @return the short class name without the package name 200 */ 201 private String getClassName( String classname ) 202 { 203 int i = classname.lastIndexOf( "." ); 204 if( i == -1 ) 205 { 206 return classname.toLowerCase(); 207 } 208 else 209 { 210 return classname.substring( i + 1, classname.length() ).toLowerCase(); 211 } 212 } 213 214 /** 215 * Return the symbolic name of component. 216 * 217 * @return the symbolic name of component. 218 */ 219 public String getName() 220 { 221 return m_name; 222 } 223 224 /** 225 * Return the component collection policy. 226 * 227 * @return the policy 228 */ 229 public CollectionPolicy getCollectionPolicy() 230 { 231 return m_collection; 232 } 233 234 /** 235 * Test is the component type implements a weak collection policy. 236 * 237 * @return TRUE if the policy is weak 238 */ 239 public boolean isWeak() 240 { 241 return m_collection.equals( CollectionPolicy.WEAK ); 242 } 243 244 /** 245 * Test is the component type implements a soft collection policy. 246 * 247 * @return TRUE if the policy is soft 248 */ 249 public boolean isSoft() 250 { 251 return m_collection.equals( CollectionPolicy.SOFT ); 252 } 253 254 /** 255 * Test is the component type implements a hard collection policy. 256 * 257 * @return TRUE if the policy is hard 258 */ 259 public boolean isHard() 260 { 261 return m_collection.equals( CollectionPolicy.HARD ); 262 } 263 264 /** 265 * Return the implementation class name for the component type. 266 * 267 * @return the implementation class name 268 */ 269 public String getClassname() 270 { 271 return m_classname; 272 } 273 274 /** 275 * Return the version of component. 276 * 277 * @return the version of component. 278 */ 279 public Version getVersion() 280 { 281 return m_version; 282 } 283 284 /** 285 * Return the component lifestyle policy. 286 * 287 * @return the lifestyle policy 288 */ 289 public LifestylePolicy getLifestylePolicy() 290 { 291 return m_lifestyle; 292 } 293 294 /** 295 * Ruturn the thread-safe status. 296 * 297 * @return the thread-safe status 298 */ 299 public boolean isThreadSafe() 300 { 301 return m_threadsafe.equals( ThreadSafePolicy.TRUE ); 302 } 303 304 /** 305 * Ruturn the thread-safe policy value. 306 * 307 * @return the thread-safe policy value 308 */ 309 public ThreadSafePolicy getThreadSafePolicy() 310 { 311 return m_threadsafe; 312 } 313 314 /** 315 * Return a string representation of the info descriptor. 316 * @return the stringified type 317 */ 318 public String toString() 319 { 320 return "[" + getName() + "] " + getClassname() + ":" + getVersion(); 321 } 322 323 /** 324 * Test is the supplied object is equal to this object. 325 * @param other the other object 326 * @return true if the object are equivalent 327 */ 328 public boolean equals( Object other ) 329 { 330 boolean isEqual = super.equals( other ) && other instanceof InfoDescriptor; 331 if( isEqual ) 332 { 333 InfoDescriptor info = (InfoDescriptor) other; 334 isEqual = isEqual && m_threadsafe.equals( info.m_threadsafe ); 335 isEqual = isEqual && m_classname.equals( info.m_classname ); 336 isEqual = isEqual && m_collection.equals( info.m_collection ); 337 isEqual = isEqual && m_name.equals( info.m_name ); 338 isEqual = isEqual && m_lifestyle.equals( info.m_lifestyle ); 339 if( null == m_version ) 340 { 341 isEqual = isEqual && null == info.m_version; 342 } 343 else 344 { 345 isEqual = isEqual && m_version.equals( info.m_version ); 346 } 347 } 348 return isEqual; 349 } 350 351 /** 352 * Return the hashcode for the object. 353 * @return the hashcode value 354 */ 355 public int hashCode() 356 { 357 int hash = super.hashCode(); 358 hash ^= m_collection.hashCode(); 359 hash ^= m_threadsafe.hashCode(); 360 hash ^= m_classname.hashCode(); 361 if ( null != m_name ) 362 { 363 hash ^= m_name.hashCode(); 364 } 365 366 if ( null != m_lifestyle ) 367 { 368 hash = hash + 9873234; 369 hash ^= m_lifestyle.hashCode(); 370 } 371 372 if ( null != m_version ) 373 { 374 hash ^= m_version.hashCode(); 375 } 376 return hash; 377 } 378 }