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.core.container;
018:
019: import java.io.InputStream;
020: import java.net.URL;
021:
022: import junit.framework.TestCase;
023:
024: import org.apache.avalon.excalibur.component.DefaultRoleManager;
025: import org.apache.avalon.excalibur.component.ExcaliburComponentManager;
026: import org.apache.avalon.excalibur.logger.LoggerManager;
027: import org.apache.avalon.framework.configuration.Configuration;
028: import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;
029: import org.apache.avalon.framework.container.ContainerUtil;
030: import org.apache.avalon.framework.context.Context;
031: import org.apache.avalon.framework.context.DefaultContext;
032: import org.apache.avalon.framework.logger.ConsoleLogger;
033: import org.apache.avalon.framework.logger.Logger;
034: import org.apache.avalon.framework.service.ServiceException;
035: import org.apache.avalon.framework.service.ServiceManager;
036: import org.apache.avalon.framework.service.WrapperServiceManager;
037: import org.apache.cocoon.util.Deprecation;
038:
039: /**
040: * JUnit TestCase for Cocoon Components.
041: * <p>
042: * This class extends the JUnit TestCase class to setup an environment which
043: * makes it possible to easily test Cocoon Components. The following methods
044: * and instance variables are exposed for convenience testing:
045: * </p>
046: * <dl>
047: * <dt>getManager()</dt>
048: * <dd>
049: * This instance variable contains an initialized service manager which
050: * can be used to lookup components configured in the test configuration
051: * file. (see below)
052: * </dd>
053: * <dt>getLogger()</dt>
054: * <dd>
055: * This method returns a logger for this test case. By default this
056: * logger logs with log level DEBUG.
057: * </dd>
058: * </dl>
059: * <p>
060: * The following test case configuration can be used as a basis for new tests.
061: * Detailed explanations of the configuration elements can be found after
062: * the example.
063: * </p>
064: * <pre>
065: * <testcase>
066: * <context>
067: * <entry name="foo" value="bar"/>
068: * <entry name="baz" class="my.context.Class"/>
069: * </context>
070: *
071: * <roles>
072: * <role name="org.apache.avalon.excalibur.datasource.DataSourceComponentSelector"
073: * shorthand="datasources"
074: * default-class="org.apache.avalon.excalibur.component.ExcaliburComponentSelector">
075: * <hint shorthand="jdbc" class="org.apache.avalon.excalibur.datasource.JdbcDataSource"/>
076: * </role>
077: * </roles>
078: *
079: * <components>
080: * <datasources>
081: * <jdbc name="personell">
082: * <pool-controller min="5" max="10"/>
083: * <jdbc name="personnel"/>
084: * <dburl>jdbc:odbc:test</dburl>
085: * <user>test</user>
086: * <password>test</password>
087: * <driver>sun.jdbc.odbc.JdbcOdbcDriver</driver>
088: * </jdbc>
089: * </datasources>
090: * </components>
091: * </testcase>
092: * </pre>
093: * <p>
094: * Element Explanation:
095: * <dl>
096: * <dt>testcase</dt>
097: * <dd>Defines a test case configuration. Must contain one each of the
098: * following elements: <code>annotation</code>,
099: * <code>context</code>, <code>roles</code>, and <code>components</code>
100: * </dd>.
101: *
102: * <dt>context</dt>
103: * <dd>Allows context properties to be set in the context passed to any
104: * Contextualizable components.</dd>
105: *
106: * <dt>roles</dt>
107: * <dd>Roles configuration for the components configured in the
108: * <code>components</code> element.
109: * </dd>
110: *
111: * <dt>components</dt>
112: * <dd>Used to configure any Components used by the test cases.
113: * </dd>
114: *
115: * </dl>
116: *
117: * @version $Id: $
118: */
119: public class ContainerTestCase extends TestCase {
120:
121: /** The default logger */
122: private Logger logger;
123:
124: /** The service manager to use */
125: private ServiceManager manager;
126:
127: /** Return the logger */
128: protected Logger getLogger() {
129: return logger;
130: }
131:
132: /** Return the service manager */
133: protected ServiceManager getManager() {
134: return this .manager;
135: }
136:
137: /* (non-Javadoc)
138: * @see junit.framework.TestCase#setUp()
139: */
140: protected void setUp() throws Exception {
141: super .setUp();
142:
143: String level = System.getProperty("junit.test.loglevel", ""
144: + ConsoleLogger.LEVEL_WARN);
145: this .logger = new ConsoleLogger(Integer.parseInt(level));
146: Deprecation.setLogger(this .logger);
147: prepare();
148: }
149:
150: /**
151: * Initializes the ComponentLocator
152: *
153: * The configuration file is determined by the class name plus .xtest appended,
154: * all '.' replaced by '/' and loaded as a resource via classpath
155: */
156: protected void prepare() throws Exception {
157: final String resourceName = getClass().getName().replace('.',
158: '/')
159: + ".xtest";
160: URL resource = getClass().getClassLoader().getResource(
161: resourceName);
162:
163: if (resource != null) {
164: getLogger().debug("Loading resource " + resourceName);
165: prepare(resource.openStream());
166: } else {
167: getLogger().debug("Resource not found " + resourceName);
168: }
169: }
170:
171: /**
172: * Initializes the ComponentLocator
173: *
174: * @param testconf The configuration file is passed as a <code>InputStream</code>
175: *
176: * A common way to supply a InputStream is to overwrite the initialize() method
177: * in the sub class, do there whatever is needed to get the right InputStream object
178: * supplying a conformant xtest configuartion and pass it to this initialize method.
179: * the mentioned initialize method is also the place to set a different logging priority
180: * to the member variable m_logPriority.
181: */
182: protected final void prepare(final InputStream testconf)
183: throws Exception {
184: if (getLogger().isDebugEnabled()) {
185: getLogger().debug("ContainerTestCase.initialize");
186: }
187:
188: final DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder();
189: final Configuration conf = builder.build(testconf);
190:
191: Context context = this .setupContext(conf.getChild("context"));
192:
193: setupManagers(conf.getChild("components"), conf
194: .getChild("roles"), context);
195: }
196:
197: /* (non-Javadoc)
198: * @see junit.framework.TestCase#tearDown()
199: */
200: protected void tearDown() throws Exception {
201: done();
202: super .tearDown();
203: }
204:
205: /**
206: * Disposes the <code>ComponentLocator</code>
207: */
208: final private void done() {
209: if (manager != null) {
210: ContainerUtil.dispose(manager);
211: manager = null;
212: }
213: }
214:
215: /**
216: * set up a context according to the xtest configuration specifications context
217: * element.
218: *
219: * A method addContext(DefaultContext context) is called here to enable subclasses
220: * to put additional objects into the context programmatically.
221: */
222: final private Context setupContext(final Configuration conf)
223: throws Exception {
224: final DefaultContext context = new DefaultContext();
225: final Configuration[] confs = conf.getChildren("entry");
226: for (int i = 0; i < confs.length; i++) {
227: final String key = confs[i].getAttribute("name");
228: final String value = confs[i].getAttribute("value", null);
229: if (value == null) {
230: String clazz = confs[i].getAttribute("class");
231: Object obj = getClass().getClassLoader().loadClass(
232: clazz).newInstance();
233: context.put(key, obj);
234: if (getLogger().isInfoEnabled()) {
235: getLogger().info(
236: "ContainerTestCase: added an instance of class "
237: + clazz + " to context entry "
238: + key);
239: }
240: } else {
241: context.put(key, value);
242: if (getLogger().isInfoEnabled()) {
243: getLogger().info(
244: "ContainerTestCase: added value \"" + value
245: + "\" to context entry " + key);
246: }
247: }
248: }
249: addContext(context);
250: return context;
251: }
252:
253: /**
254: * This method may be overwritten by subclasses to put additional objects
255: * into the context programmatically.
256: */
257: protected void addContext(DefaultContext context) {
258: }
259:
260: final private void setupManagers(final Configuration confCM,
261: final Configuration confRM, final Context context)
262: throws Exception {
263: // Setup the RoleManager
264: DefaultRoleManager roleManager = new DefaultRoleManager();
265: roleManager.enableLogging(getLogger());
266: roleManager.configure(confRM);
267:
268: // Set up the ComponentLocator
269: ExcaliburComponentManager ecManager = new ExcaliburComponentManager();
270: ecManager.enableLogging(getLogger());
271: ecManager.contextualize(context);
272: ecManager.setRoleManager(roleManager);
273: ecManager
274: .setLoggerManager(new DefaultLoggerManager(getLogger()));
275: ecManager.configure(confCM);
276: ecManager.initialize();
277: this .manager = new WrapperServiceManager(ecManager);
278: }
279:
280: protected final Object lookup(final String key)
281: throws ServiceException {
282: return manager.lookup(key);
283: }
284:
285: protected final void release(final Object object) {
286: manager.release(object);
287: }
288:
289: protected static class DefaultLoggerManager implements
290: LoggerManager {
291: private Logger logger;
292:
293: public DefaultLoggerManager(Logger logger) {
294: this .logger = logger;
295: }
296:
297: /* (non-Javadoc)
298: * @see org.apache.avalon.excalibur.logger.LoggerManager#getDefaultLogger()
299: */
300: public Logger getDefaultLogger() {
301: return this .logger;
302: }
303:
304: /* (non-Javadoc)
305: * @see org.apache.avalon.excalibur.logger.LoggerManager#getLoggerForCategory(java.lang.String)
306: */
307: public Logger getLoggerForCategory(String arg0) {
308: return this.logger;
309: }
310: }
311: }
|