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 }