View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.common.component;
25  
26  import java.util.ArrayList;
27  import java.util.Collections;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  import net.jcip.annotations.Immutable;
32  import org.modeshape.common.CommonI18n;
33  import org.modeshape.common.annotation.Category;
34  import org.modeshape.common.annotation.Description;
35  import org.modeshape.common.annotation.Label;
36  import org.modeshape.common.util.CheckArg;
37  import org.modeshape.common.util.ClassUtil;
38  
39  /**
40   * An immutable configuration for a {@link Component}.
41   */
42  @Immutable
43  public class ComponentConfig implements Comparable<ComponentConfig> {
44  
45      @Description( i18n = CommonI18n.class, value = "componentConfigNamePropertyDescription" )
46      @Label( i18n = CommonI18n.class, value = "componentConfigNamePropertyLabel" )
47      @Category( i18n = CommonI18n.class, value = "componentConfigNamePropertyCategory" )
48      private final String name;
49  
50      @Description( i18n = CommonI18n.class, value = "componentConfigDescriptionPropertyDescription" )
51      @Label( i18n = CommonI18n.class, value = "componentConfigDescriptionPropertyLabel" )
52      @Category( i18n = CommonI18n.class, value = "componentConfigDescriptionPropertyCategory" )
53      private final String description;
54  
55      @Description( i18n = CommonI18n.class, value = "componentConfigClassnamePropertyDescription" )
56      @Label( i18n = CommonI18n.class, value = "componentConfigClassnamePropertyLabel" )
57      @Category( i18n = CommonI18n.class, value = "componentConfigClassnamePropertyCategory" )
58      private final String componentClassname;
59  
60      // @Description( i18n = CommonI18n.class, value = "componentConfigClasspathPropertyDescription" )
61      // @Label( i18n = CommonI18n.class, value = "componentConfigClasspathPropertyLabel" )
62      // @Category( i18n = CommonI18n.class, value = "componentConfigClasspathPropertyCategory" )
63      private final List<String> classpath;
64  
65      private final Map<String, Object> properties;
66      private final long timestamp;
67  
68      /**
69       * Create a component configuration.
70       * 
71       * @param name the name of the configuration, which is considered to be a unique identifier
72       * @param description the description
73       * @param classname the name of the Java class used for the component
74       * @param classpath the optional classpath (defined in a way compatible with a {@link ClassLoaderFactory}
75       * @throws IllegalArgumentException if the name is null, empty or blank, or if the classname is null, empty or not a valid
76       *         Java classname
77       */
78      public ComponentConfig( String name,
79                              String description,
80                              String classname,
81                              String... classpath ) {
82          this(name, description, System.currentTimeMillis(), Collections.<String, Object>emptyMap(), classname, classpath);
83      }
84  
85      /**
86       * Create a component configuration.
87       * 
88       * @param name the name of the configuration, which is considered to be a unique identifier
89       * @param description the description
90       * @param properties the mapping of properties to values that should be set through reflection after a component is
91       *        instantiated with this configuration information
92       * @param classname the name of the Java class used for the component
93       * @param classpath the optional classpath (defined in a way compatible with a {@link ClassLoaderFactory}
94       * @throws IllegalArgumentException if the name is null, empty or blank, or if the class name is null, empty or not a valid
95       *         Java class name
96       */
97      public ComponentConfig( String name,
98                              String description,
99                              Map<String, Object> properties,
100                             String classname,
101                             String... classpath ) {
102         this(name, description, System.currentTimeMillis(), properties, classname, classpath);
103     }
104 
105     /**
106      * Create a component configuration.
107      * 
108      * @param name the name of the configuration, which is considered to be a unique identifier
109      * @param description the description
110      * @param timestamp the timestamp that this component was last changed
111      * @param properties the mapping of properties to values that should be set through reflection after a component is
112      *        instantiated with this configuration information
113      * @param classname the name of the Java class used for the component
114      * @param classpath the optional classpath (defined in a way compatible with a {@link ClassLoaderFactory}
115      * @throws IllegalArgumentException if the name is null, empty or blank, or if the classname is null, empty or not a valid
116      *         Java classname
117      */
118     public ComponentConfig( String name,
119                             String description,
120                             long timestamp,
121                             Map<String, Object> properties,
122                             String classname,
123                             String... classpath ) {
124         CheckArg.isNotEmpty(name, "name");
125         this.name = name.trim();
126         this.description = description != null ? description.trim() : "";
127         this.componentClassname = classname;
128         this.classpath = buildList(classpath);
129         this.timestamp = timestamp;
130         this.properties = properties != null ? Collections.unmodifiableMap(new HashMap<String, Object>(properties)) : Collections.<String, Object>emptyMap();
131 
132         // Check the classname is a valid classname ...
133         if (!ClassUtil.isFullyQualifiedClassname(classname)) {
134             throw new IllegalArgumentException(CommonI18n.componentClassnameNotValid.text(classname, name));
135         }
136     }
137 
138     /* package */static List<String> buildList( String... classpathElements ) {
139         List<String> classpath = null;
140         if (classpathElements != null) {
141             classpath = new ArrayList<String>();
142             for (String classpathElement : classpathElements) {
143                 if (!classpath.contains(classpathElement)) classpath.add(classpathElement);
144             }
145             classpath = Collections.unmodifiableList(classpath);
146         } else {
147             classpath = Collections.emptyList(); // already immutable
148         }
149         return classpath;
150     }
151 
152     /**
153      * Get the name of this component.
154      * 
155      * @return the component name; never null, empty or blank
156      */
157     public String getName() {
158         return this.name;
159     }
160 
161     /**
162      * Get the description for this component
163      * 
164      * @return the description
165      */
166     public String getDescription() {
167         return this.description;
168     }
169 
170     /**
171      * Get the fully-qualified name of the Java class used for instances of this component
172      * 
173      * @return the Java class name of this component; never null or empty and always a valid Java class name
174      */
175     public String getComponentClassname() {
176         return this.componentClassname;
177     }
178 
179     /**
180      * Get the classpath defined in terms of strings compatible with a {@link ClassLoaderFactory}.
181      * 
182      * @return the classpath; never null but possibly empty
183      */
184     public List<String> getComponentClasspath() {
185         return this.classpath;
186     }
187 
188     /**
189      * Get the classpath defined as an array of strings compatible with a {@link ClassLoaderFactory}.
190      * 
191      * @return the classpath as an array; never null but possibly empty
192      */
193     public String[] getComponentClasspathArray() {
194         return this.classpath.toArray(new String[this.classpath.size()]);
195     }
196 
197     /**
198      * Get the system timestamp when this configuration object was created.
199      * 
200      * @return the timestamp
201      */
202     public long getTimestamp() {
203         return this.timestamp;
204     }
205 
206     /**
207      * Get the (unmodifiable) properties to be set through reflection on components of this type after instantiation
208      * 
209      * @return the properties to be set through reflection on components of this type after instantiation; never null
210      */
211     public Map<String, Object> getProperties() {
212         return this.properties;
213     }
214 
215     /**
216      * {@inheritDoc}
217      */
218     public int compareTo( ComponentConfig that ) {
219         if (that == this) return 0;
220         int diff = this.getName().compareToIgnoreCase(that.getName());
221         if (diff != 0) return diff;
222         diff = (int)(this.getTimestamp() - that.getTimestamp());
223         return diff;
224     }
225 
226     /**
227      * {@inheritDoc}
228      */
229     @Override
230     public int hashCode() {
231         return this.getName().hashCode();
232     }
233 
234     /**
235      * {@inheritDoc}
236      */
237     @Override
238     public boolean equals( Object obj ) {
239         if (obj == this) return true;
240         if (obj instanceof ComponentConfig) {
241             ComponentConfig that = (ComponentConfig)obj;
242             if (!this.getClass().equals(that.getClass())) return false;
243             return this.getName().equalsIgnoreCase(that.getName());
244         }
245         return false;
246     }
247 
248     /**
249      * Determine whether this component has changed with respect to the supplied component. This method basically checks all
250      * attributes, whereas {@link #equals(Object) equals} only checks the {@link #getClass() type} and {@link #getName()}.
251      * 
252      * @param component the component to be compared with this one
253      * @return true if this componet and the supplied component have some changes, or false if they are exactly equivalent
254      * @throws IllegalArgumentException if the supplied component reference is null or is not the same {@link #getClass() type} as
255      *         this object
256      */
257     public boolean hasChanged( ComponentConfig component ) {
258         CheckArg.isNotNull(component, "component");
259         CheckArg.isInstanceOf(component, this.getClass(), "component");
260         if (!this.getName().equalsIgnoreCase(component.getName())) return true;
261         if (!this.getDescription().equals(component.getDescription())) return true;
262         if (!this.getComponentClassname().equals(component.getComponentClassname())) return true;
263         if (!this.getComponentClasspath().equals(component.getComponentClasspath())) return true;
264         return false;
265     }
266 
267 }