001 /* 002 * JBoss DNA (http://www.jboss.org/dna) 003 * See the COPYRIGHT.txt file distributed with this work for information 004 * regarding copyright ownership. Some portions may be licensed 005 * to Red Hat, Inc. under one or more contributor license agreements. 006 * See the AUTHORS.txt file in the distribution for a full listing of 007 * individual contributors. 008 * 009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA 010 * is licensed to you under the terms of the GNU Lesser General Public License as 011 * published by the Free Software Foundation; either version 2.1 of 012 * the License, or (at your option) any later version. 013 * 014 * JBoss DNA is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 * Lesser General Public License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this software; if not, write to the Free 021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 023 */ 024 package org.jboss.dna.common.component; 025 026 import java.util.ArrayList; 027 import java.util.Collections; 028 import java.util.HashMap; 029 import java.util.List; 030 import java.util.Map; 031 import net.jcip.annotations.Immutable; 032 import org.jboss.dna.common.CommonI18n; 033 import org.jboss.dna.common.util.CheckArg; 034 import org.jboss.dna.common.util.ClassUtil; 035 036 /** 037 * @author Randall Hauch 038 */ 039 @Immutable 040 public class ComponentConfig implements Comparable<ComponentConfig> { 041 042 private final String name; 043 private final String description; 044 private final String componentClassname; 045 private final List<String> classpath; 046 private final Map<String, Object> properties; 047 private final long timestamp; 048 049 /** 050 * Create a component configuration. 051 * 052 * @param name the name of the configuration, which is considered to be a unique identifier 053 * @param description the description 054 * @param classname the name of the Java class used for the component 055 * @param classpath the optional classpath (defined in a way compatible with a {@link ClassLoaderFactory} 056 * @throws IllegalArgumentException if the name is null, empty or blank, or if the classname is null, empty or not a valid 057 * Java classname 058 */ 059 public ComponentConfig( String name, 060 String description, 061 String classname, 062 String... classpath ) { 063 this(name, description, System.currentTimeMillis(), Collections.<String, Object>emptyMap(), classname, classpath); 064 } 065 066 /** 067 * Create a component configuration. 068 * 069 * @param name the name of the configuration, which is considered to be a unique identifier 070 * @param description the description 071 * @param properties the mapping of properties to values that should be set through reflection after a component is 072 * instantiated with this configuration information 073 * @param classname the name of the Java class used for the component 074 * @param classpath the optional classpath (defined in a way compatible with a {@link ClassLoaderFactory} 075 * @throws IllegalArgumentException if the name is null, empty or blank, or if the class name is null, empty or not a valid 076 * Java class name 077 */ 078 public ComponentConfig( String name, 079 String description, 080 Map<String, Object> properties, 081 String classname, 082 String... classpath ) { 083 this(name, description, System.currentTimeMillis(), properties, classname, classpath); 084 } 085 086 /** 087 * Create a component configuration. 088 * 089 * @param name the name of the configuration, which is considered to be a unique identifier 090 * @param description the description 091 * @param timestamp the timestamp that this component was last changed 092 * @param properties the mapping of properties to values that should be set through reflection after a component is 093 * instantiated with this configuration information 094 * @param classname the name of the Java class used for the component 095 * @param classpath the optional classpath (defined in a way compatible with a {@link ClassLoaderFactory} 096 * @throws IllegalArgumentException if the name is null, empty or blank, or if the classname is null, empty or not a valid 097 * Java classname 098 */ 099 public ComponentConfig( String name, 100 String description, 101 long timestamp, 102 Map<String, Object> properties, 103 String classname, 104 String... classpath ) { 105 CheckArg.isNotEmpty(name, "name"); 106 this.name = name.trim(); 107 this.description = description != null ? description.trim() : ""; 108 this.componentClassname = classname; 109 this.classpath = buildList(classpath); 110 this.timestamp = timestamp; 111 this.properties = properties != null ? Collections.unmodifiableMap(new HashMap<String, Object>(properties)) : Collections.<String, Object>emptyMap(); 112 113 // Check the classname is a valid classname ... 114 if (!ClassUtil.isFullyQualifiedClassname(classname)) { 115 throw new IllegalArgumentException(CommonI18n.componentClassnameNotValid.text(classname, name)); 116 } 117 } 118 119 /* package */static List<String> buildList( String... classpathElements ) { 120 List<String> classpath = null; 121 if (classpathElements != null) { 122 classpath = new ArrayList<String>(); 123 for (String classpathElement : classpathElements) { 124 if (!classpath.contains(classpathElement)) classpath.add(classpathElement); 125 } 126 classpath = Collections.unmodifiableList(classpath); 127 } else { 128 classpath = Collections.emptyList(); // already immutable 129 } 130 return classpath; 131 } 132 133 /** 134 * Get the name of this component. 135 * 136 * @return the component name; never null, empty or blank 137 */ 138 public String getName() { 139 return this.name; 140 } 141 142 /** 143 * Get the description for this component 144 * 145 * @return the description 146 */ 147 public String getDescription() { 148 return this.description; 149 } 150 151 /** 152 * Get the fully-qualified name of the Java class used for instances of this component 153 * 154 * @return the Java class name of this component; never null or empty and always a valid Java class name 155 */ 156 public String getComponentClassname() { 157 return this.componentClassname; 158 } 159 160 /** 161 * Get the classpath defined in terms of strings compatible with a {@link ClassLoaderFactory}. 162 * 163 * @return the classpath; never null but possibly empty 164 */ 165 public List<String> getComponentClasspath() { 166 return this.classpath; 167 } 168 169 /** 170 * Get the classpath defined as an array of strings compatible with a {@link ClassLoaderFactory}. 171 * 172 * @return the classpath as an array; never null but possibly empty 173 */ 174 public String[] getComponentClasspathArray() { 175 return this.classpath.toArray(new String[this.classpath.size()]); 176 } 177 178 /** 179 * Get the system timestamp when this configuration object was created. 180 * 181 * @return the timestamp 182 */ 183 public long getTimestamp() { 184 return this.timestamp; 185 } 186 187 /** 188 * Get the (unmodifiable) properties to be set through reflection on components of this type after instantiation 189 * 190 * @return the properties to be set through reflection on components of this type after instantiation; never null 191 */ 192 public Map<String, Object> getProperties() { 193 return this.properties; 194 } 195 196 /** 197 * {@inheritDoc} 198 */ 199 public int compareTo( ComponentConfig that ) { 200 if (that == this) return 0; 201 int diff = this.getName().compareToIgnoreCase(that.getName()); 202 if (diff != 0) return diff; 203 diff = (int)(this.getTimestamp() - that.getTimestamp()); 204 return diff; 205 } 206 207 /** 208 * {@inheritDoc} 209 */ 210 @Override 211 public int hashCode() { 212 return this.getName().hashCode(); 213 } 214 215 /** 216 * {@inheritDoc} 217 */ 218 @Override 219 public boolean equals( Object obj ) { 220 if (obj == this) return true; 221 if (obj instanceof ComponentConfig) { 222 ComponentConfig that = (ComponentConfig)obj; 223 if (!this.getClass().equals(that.getClass())) return false; 224 return this.getName().equalsIgnoreCase(that.getName()); 225 } 226 return false; 227 } 228 229 /** 230 * Determine whether this component has changed with respect to the supplied component. This method basically checks all 231 * attributes, whereas {@link #equals(Object) equals} only checks the {@link #getClass() type} and {@link #getName()}. 232 * 233 * @param component the component to be compared with this one 234 * @return true if this componet and the supplied component have some changes, or false if they are exactly equivalent 235 * @throws IllegalArgumentException if the supplied component reference is null or is not the same {@link #getClass() type} as 236 * this object 237 */ 238 public boolean hasChanged( ComponentConfig component ) { 239 CheckArg.isNotNull(component, "component"); 240 CheckArg.isInstanceOf(component, this.getClass(), "component"); 241 if (!this.getName().equalsIgnoreCase(component.getName())) return true; 242 if (!this.getDescription().equals(component.getDescription())) return true; 243 if (!this.getComponentClassname().equals(component.getComponentClassname())) return true; 244 if (!this.getComponentClasspath().equals(component.getComponentClasspath())) return true; 245 return false; 246 } 247 248 }