001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components;
018:
019: import org.apache.avalon.excalibur.component.ExcaliburComponentSelector;
020: import org.apache.avalon.excalibur.component.RoleManager;
021: import org.apache.avalon.framework.component.Component;
022: import org.apache.avalon.framework.component.ComponentException;
023: import org.apache.avalon.framework.configuration.Configuration;
024: import org.apache.avalon.framework.configuration.ConfigurationException;
025: import org.apache.avalon.framework.configuration.DefaultConfiguration;
026:
027: /**
028: * An extension of <code>ExcaliburComponentSelector</code> that can have a parent
029: * and accepts a wider variety of configurations.
030: *
031: * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
032: * @version CVS $Id: ExtendedComponentSelector.java 433543 2006-08-22 06:22:54Z crossley $
033: */
034: public class ExtendedComponentSelector extends
035: ExcaliburComponentSelector implements ParentAware {
036:
037: /** The role manager */
038: protected RoleManager roles;
039:
040: /** The parent selector, if any */
041: protected ExtendedComponentSelector parentSelector;
042:
043: /** The parent locator, if any */
044: protected ComponentLocator parentLocator;
045:
046: /** The class loader to use */
047: protected ClassLoader classLoader;
048:
049: /** The role of this selector. Set in <code>configure()</code>. */
050: protected String roleName;
051:
052: /** The default hint */
053: protected String defaultHint;
054:
055: /** This selector's location (used for debugging purposes) */
056: private String location;
057:
058: /** Create the ComponentSelector with the Thread context ClassLoader */
059: public ExtendedComponentSelector() {
060: this .classLoader = Thread.currentThread()
061: .getContextClassLoader();
062: }
063:
064: /** Create the ComponentSelector with a ClassLoader */
065: public ExtendedComponentSelector(ClassLoader loader) {
066: super (loader);
067:
068: if (loader == null) {
069: this .classLoader = Thread.currentThread()
070: .getContextClassLoader();
071: } else {
072: this .classLoader = loader;
073: }
074: }
075:
076: /**
077: * Get the name for component-instance elements (i.e. components not defined
078: * by their role shortcut. If <code>null</code>, any element having a 'class'
079: * attribute will be considered as a component instance.
080: * <p>
081: * The default here is to return <code>null</code>, and subclasses can redefine
082: * this method to return particular values.
083: *
084: * @return <code>null</code>, but can be changed by subclasses
085: */
086: protected String getComponentInstanceName() {
087: return null;
088: }
089:
090: /**
091: * Get the name of the attribute giving the class name of a component.
092: * The default here is "class", but this can be overriden in subclasses.
093: *
094: * @return "<code>class</code>", but can be changed by subclasses
095: */
096: protected String getClassAttributeName() {
097: return "class";
098: }
099:
100: /**
101: * Get the name of the attribute giving the default hint to use if
102: * none is given. The default here is "default", but this can be
103: * overriden in subclasses. If this method returns <code>null</code>,
104: * no default hint can be specified.
105: *
106: * @return "<code>default</code>", but can be changed by subclasses
107: */
108: protected String getDefaultHintAttributeName() {
109: return "default";
110: }
111:
112: /**
113: * Configure the RoleManager. Redeclared only because parent member is private.
114: */
115: public void setRoleManager(RoleManager roles) {
116: super .setRoleManager(roles);
117: this .roles = roles;
118: }
119:
120: /**
121: * Set the parent of this selector. This can be done after the selector is
122: * initialized, but <em>only once</em>. This allows this selector to be
123: * created by a component manager while still being able to have a parent.
124: *
125: * @param parent the parent selector
126: * @throws IllegalStateException if parent is already set
127: */
128: /* public void setParentSelector(ComponentSelector parent) {
129: if (this.parentSelector != null) {
130: throw new IllegalStateException("Parent selector is already set");
131: }
132: this.parentSelector = parent;
133: this.parentComponents = new HashSet();
134: }
135: */
136:
137: /**
138: * Get the role name for this selector. This is called by <code>configure()</code>
139: * to set the value of <code>this.roleName</code>.
140: *
141: * @return the role name, or <code>null<code> if it couldn't be determined.
142: */
143: protected String getRoleName(Configuration config) {
144: // Get the role for this selector
145: String roleName = config.getAttribute("role", null);
146: if (roleName == null && this .roles != null) {
147: roleName = this .roles.getRoleForName(config.getName());
148: }
149:
150: return roleName;
151: }
152:
153: /**
154: * Configure this selector. This is the main difference with the parent class :
155: * <ul>
156: * <li>if {@link #getComponentInstanceName()} returns <code>null</code>,
157: * any child configurations having a attribute named as the result of
158: * {@link #getClassAttributeName()}, is considered as a component instance.
159: * </li>
160: * <li>if {@link #getComponentInstanceName()} returns a non-null value,
161: * only child configurations having this name are considered as a
162: * component instance.
163: * </li>
164: * <li>if other cases, it's name is considered to be a hint in the role manager.
165: * The behaviour is then the same as <code>ExcaliburComponentSelector</code>.
166: * </li>
167: *
168: * @param config the configuration
169: * @throws ConfigurationException if some hints aren't defined
170: */
171: public void configure(Configuration config)
172: throws ConfigurationException {
173:
174: // Store location
175: this .location = config.getLocation();
176:
177: this .roleName = getRoleName(config);
178:
179: // Pass a copy of the top-level object to superclass so that
180: // our name is properly initialized
181: // FIXME : could be avoided if parent m_role was protected or had protected accessors
182: DefaultConfiguration temp = new DefaultConfiguration(config
183: .getName(), this .location);
184: if (config.getAttribute("role", null) != null) {
185: temp.setAttribute("role", this .roleName);
186: }
187: super .configure(temp);
188:
189: // Get default hint
190: this .defaultHint = config.getAttribute(this
191: .getDefaultHintAttributeName(), null);
192:
193: // Add components
194: String compInstanceName = getComponentInstanceName();
195:
196: Configuration[] instances = config.getChildren();
197:
198: for (int i = 0; i < instances.length; i++) {
199: Configuration instance = instances[i];
200:
201: Object hint = instance.getAttribute("name").trim();
202:
203: String classAttr = instance.getAttribute(
204: getClassAttributeName(), null);
205: String className;
206:
207: if (compInstanceName == null) {
208: // component-instance implicitly defined by the presence of the 'class' attribute
209: if (classAttr == null) {
210: className = this .roles.getDefaultClassNameForHint(
211: roleName, instance.getName());
212: } else {
213: className = classAttr.trim();
214: }
215:
216: } else {
217: // component-instances names explicitly defined
218: if (compInstanceName.equals(instance.getName())) {
219: className = (classAttr == null) ? null : classAttr
220: .trim();
221: } else {
222: className = this .roles.getDefaultClassNameForHint(
223: roleName, instance.getName());
224: }
225: }
226:
227: if (className == null) {
228: String message = "Unable to determine class name for component named '"
229: + hint + "' at " + instance.getLocation();
230:
231: getLogger().error(message);
232: throw new ConfigurationException(message);
233: }
234:
235: try {
236: Class clazz = this .classLoader.loadClass(className);
237: addComponent(hint, clazz, instance);
238:
239: } catch (Exception e) {
240: String message = "Could not load class " + className
241: + " for component named '" + hint + "' at "
242: + instance.getLocation();
243:
244: getLogger().error(message, e);
245: throw new ConfigurationException(message, e);
246: }
247: }
248: }
249:
250: /**
251: * Get the default hint, if any for this selector.
252: */
253: public String getDefaultHint() {
254: // Inherit parent default hint if have no own
255: if (this .defaultHint == null && this .parentSelector != null) {
256: return this .parentSelector.getDefaultHint();
257: }
258:
259: return this .defaultHint;
260: }
261:
262: /* (non-Javadoc)
263: * @see org.apache.avalon.framework.component.ComponentSelector#select(java.lang.Object)
264: */
265: public Component select(Object hint) throws ComponentException {
266: if (hint == null) {
267: hint = this .defaultHint;
268: }
269:
270: if (parentSelector == null) {
271: // No parent: default behaviour
272: return super .select(hint);
273: }
274:
275: try {
276: // Try in this selector first
277: final Component component = super .select(hint);
278: return component;
279:
280: } catch (ComponentException original) {
281: try {
282: // Doesn't exist here: try in parent selector
283: final Component component = this .parentSelector
284: .select(hint);
285: return component;
286:
287: } catch (ComponentException nested) {
288: // Doesn't exist in parent too: throw exception.
289:
290: if (nested.getCause() != null) {
291: // Nested exception has a cause; let's throw it instead of original.
292: throw nested;
293: }
294:
295: // Throw original exception
296: throw original;
297: }
298: }
299: }
300:
301: /* (non-Javadoc)
302: * @see org.apache.avalon.framework.component.ComponentSelector#release(org.apache.avalon.framework.component.Component)
303: */
304: public void release(Component component) {
305: // Was it selected on the parent ?
306: if (this .parentSelector != null
307: && this .parentSelector.canRelease(component)) {
308: // Yes
309: this .parentSelector.release(component);
310: } else {
311: // No
312: super .release(component);
313: }
314: }
315:
316: /**
317: * Does this selector or its parent have the given hint ?
318: */
319: public boolean hasComponent(Object hint) {
320: boolean exists = super .hasComponent(hint);
321: if (!exists && this .parentSelector != null) {
322: exists = this .parentSelector.hasComponent(hint);
323: }
324: return exists;
325: }
326:
327: /**
328: * Does this selector declare a given hint? Check is performed on the components declared for this
329: * selector only, and <strong>not</strong> those potentially inherited from the parent selector.
330: *
331: * @param hint the hint to check for
332: * @return <code>true</code> if this selector has the specified hint
333: */
334: protected boolean hasDeclaredComponent(Object hint) {
335: return super .hasComponent(hint);
336: }
337:
338: /* (non-Javadoc)
339: * @see org.apache.cocoon.components.ParentAware#setParentInformation(org.apache.avalon.framework.component.ComponentManager, java.lang.String)
340: */
341: public void setParentLocator(ComponentLocator locator)
342: throws ComponentException {
343: if (this .parentSelector != null) {
344: throw new ComponentException(null,
345: "Parent selector is already set");
346: }
347: this .parentLocator = locator;
348: this .parentSelector = (ExtendedComponentSelector) locator
349: .lookup();
350: }
351:
352: /* (non-Javadoc)
353: * @see org.apache.avalon.framework.activity.Disposable#dispose()
354: */
355: public void dispose() {
356: super .dispose();
357: if (this .parentLocator != null) {
358: this .parentLocator.release(this .parentSelector);
359: this .parentLocator = null;
360: this .parentSelector = null;
361: }
362: }
363:
364: /* (non-Javadoc)
365: * @see org.apache.avalon.excalibur.component.ExcaliburComponentSelector#canRelease(org.apache.avalon.framework.component.Component)
366: */
367: protected boolean canRelease(Component component) {
368: if (this .parentSelector != null
369: && this .parentSelector.canRelease(component)) {
370: return true;
371: }
372:
373: return super.canRelease(component);
374: }
375: }
|