001: /*
002: * ========================================================================
003: *
004: * Copyright 2003-2005 The Apache Software Foundation.
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * ========================================================================
019: */
020: package org.apache.cactus.integration.ant.container;
021:
022: import java.io.File;
023: import java.io.IOException;
024:
025: import org.apache.cactus.integration.ant.deployment.DeployableFile;
026: import org.apache.cactus.integration.ant.util.AntLog;
027: import org.apache.cactus.integration.ant.util.AntTaskFactory;
028: import org.apache.commons.logging.Log;
029: import org.apache.tools.ant.BuildException;
030: import org.apache.tools.ant.ProjectComponent;
031: import org.apache.tools.ant.Task;
032: import org.apache.tools.ant.filters.ReplaceTokens;
033: import org.apache.tools.ant.taskdefs.Delete;
034: import org.apache.tools.ant.types.FileSet;
035: import org.apache.tools.ant.types.FilterChain;
036: import org.apache.tools.ant.types.Path;
037: import org.apache.tools.ant.types.PatternSet;
038: import org.apache.tools.ant.types.Environment.Variable;
039: import org.apache.tools.ant.types.selectors.SelectorUtils;
040:
041: /**
042: * Abstract base class for supporting specific containers as nested elements in
043: * the {@link org.apache.cactus.integration.ant.CactusTask}.
044: *
045: * @version $Id: AbstractContainer.java 239130 2005-01-29 15:49:18Z vmassol $
046: */
047: public abstract class AbstractContainer extends ProjectComponent
048: implements Container {
049: // Constants ---------------------------------------------------------------
050:
051: /**
052: * The path under which the container resources are stored in the JAR.
053: */
054: protected static final String RESOURCE_PATH = "/org/apache/cactus/integration/ant/container/resources/";
055:
056: // Instance Variables ------------------------------------------------------
057:
058: /**
059: * The WAR or EAR that should be deployed to the container.
060: */
061: private DeployableFile deployableFile;
062:
063: /**
064: * A pattern set which lists patterns for names of test cases that are to be
065: * excluded from a specific container.
066: */
067: private PatternSet patternSet = new PatternSet();
068:
069: /**
070: * The directory to which the test reports should be written.
071: */
072: private File toDir;
073:
074: /**
075: * Name of a property that must exist in the project if tests are to be run
076: * on the container.
077: */
078: private String ifCondition;
079:
080: /**
081: * Name of a property that must not exist in the project if tests are to be
082: * run on the container.
083: */
084: private String unlessCondition;
085:
086: /**
087: * The factory for creating ant tasks.
088: */
089: private AntTaskFactory antTaskFactory;
090:
091: /**
092: * The log to use.
093: */
094: private Log log = AntLog.NULL;
095:
096: /**
097: * List of system properties to set in the container JVM.
098: */
099: private Variable[] systemProperties;
100:
101: /**
102: * The time to sleep after the container has started up.
103: */
104: private long startUpWait = 1000;
105:
106: /**
107: * Additional classpath entries for the classpath that will be used to
108: * start the containers.
109: */
110: private Path containerClasspath;
111:
112: /**
113: * The server (name or ip) the container is living on
114: */
115: private String server = "localhost";
116:
117: /**
118: * The protocol the container is using
119: */
120: private String protocol = "http";
121:
122: // Public Methods ----------------------------------------------------------
123:
124: /**
125: * @see Container#getTestContext()
126: */
127: public String getTestContext() {
128: return null;
129: }
130:
131: /**
132: * Sets the time to wait after the container has been started up.
133: *
134: * The default time is 1 second.
135: *
136: * Note: This is a hack while waiting for container specific solutions
137: * that tell exactly when the server is started or not. ATM, the only known
138: * issue is with JBoss, where the servlet engine is started before the full
139: * JBoss is started and thus it may happen that we try to shutdown JBoss
140: * before it has finished starting, leading to an exception.
141: *
142: * @param theStartUpWait The time to wait in milliseconds
143: */
144: public void setStartUpWait(long theStartUpWait) {
145: this .startUpWait = theStartUpWait;
146: }
147:
148: /**
149: * Creates a nested exclude element that is added to the pattern set.
150: *
151: * @return The created exclude element
152: */
153: public final PatternSet.NameEntry createExclude() {
154: return this .patternSet.createExclude();
155: }
156:
157: /**
158: * Returns the exclude patterns.
159: *
160: * @return The exclude patterns
161: */
162: public final String[] getExcludePatterns() {
163: return this .patternSet.getExcludePatterns(getProject());
164: }
165:
166: /**
167: * Sets the name of a property that must exist in the project if tests are
168: * to be run on the container.
169: *
170: * @param theIfCondition The property name to set
171: */
172: public final void setIf(String theIfCondition) {
173: this .ifCondition = theIfCondition;
174: }
175:
176: /**
177: * Sets the directory to which the test reports should be written.
178: *
179: * @param theToDir The output directory to set
180: */
181: public final void setToDir(File theToDir) {
182: this .toDir = theToDir;
183: }
184:
185: /**
186: * Sets the name of a property that must not exist in the project if tests
187: * are to be run on the container.
188: *
189: * @param theUnlessCondition The property name to set
190: */
191: public final void setUnless(String theUnlessCondition) {
192: this .unlessCondition = theUnlessCondition;
193: }
194:
195: /**
196: * Sets the server (name or ip) to which the container is living.
197: *
198: * @param theServer The server to set
199: */
200: public final void setServer(String theServer) {
201: this .server = theServer;
202: }
203:
204: /**
205: * Sets the protocol the container should use
206: *
207: * @param theProtocol The protocol to set
208: */
209: public final void setProtocol(String theProtocol) {
210: this .protocol = theProtocol;
211: }
212:
213: // Container Implementation ------------------------------------------------
214:
215: /**
216: * @see Container#getStartUpWait
217: */
218: public long getStartUpWait() {
219: return this .startUpWait;
220: }
221:
222: /**
223: * @see Container#getToDir
224: */
225: public final File getToDir() {
226: return this .toDir;
227: }
228:
229: /**
230: * The default implementation does nothing.
231: *
232: * @see Container#init
233: */
234: public void init() {
235: // The default implementation doesn nothing
236: }
237:
238: /**
239: * @see Container#isEnabled
240: */
241: public final boolean isEnabled() {
242: return (testIfCondition() && testUnlessCondition());
243: }
244:
245: /**
246: * @see Container#isExcluded
247: */
248: public final boolean isExcluded(String theTestName) {
249: String[] excludePatterns = this .patternSet
250: .getExcludePatterns(getProject());
251: if (excludePatterns != null) {
252: String testPath = theTestName.replace('.', '/');
253: for (int i = 0; i < excludePatterns.length; i++) {
254: String excludePattern = excludePatterns[i];
255: if (excludePattern.endsWith(".java")
256: || excludePattern.endsWith(".class")) {
257: excludePattern = excludePattern.substring(0,
258: excludePattern.lastIndexOf('.'));
259: }
260: if (SelectorUtils.matchPath(excludePattern, testPath)) {
261: return true;
262: }
263: }
264: }
265: return false;
266: }
267:
268: /**
269: * @see Container#setAntTaskFactory
270: */
271: public final void setAntTaskFactory(AntTaskFactory theFactory) {
272: this .antTaskFactory = theFactory;
273: }
274:
275: /**
276: * @see Container#setDeployableFile
277: */
278: public final void setDeployableFile(DeployableFile theDeployableFile) {
279: this .deployableFile = theDeployableFile;
280: }
281:
282: /**
283: * @see Container#setLog
284: */
285: public final void setLog(Log theLog) {
286: this .log = theLog;
287: }
288:
289: /**
290: * @see Container#setSystemProperties
291: */
292: public void setSystemProperties(Variable[] theProperties) {
293: this .systemProperties = theProperties;
294: }
295:
296: /**
297: * @see Container#getSystemProperties
298: */
299: public Variable[] getSystemProperties() {
300: return this .systemProperties;
301: }
302:
303: /**
304: * @see Container#setContainerClasspath(Path)
305: * @since Cactus 1.6
306: */
307: public void setContainerClasspath(Path theClasspath) {
308: this .containerClasspath = theClasspath;
309: }
310:
311: /**
312: * @see Container#getContainerClasspath()
313: * @since Cactus 1.6
314: */
315: public Path getContainerClasspath() {
316: return this .containerClasspath;
317: }
318:
319: /**
320: * @see Container#getServer()
321: */
322: public final String getServer() {
323: return this .server;
324: }
325:
326: /**
327: * @see Container#getProtocol()
328: */
329: public final String getProtocol() {
330: return this .protocol;
331: }
332:
333: /**
334: * @see Container#getBaseURL()
335: */
336: public final String getBaseURL() {
337: return this .getProtocol() + "://" + this .getServer() + ":"
338: + this .getPort();
339: }
340:
341: // Protected Methods -------------------------------------------------------
342:
343: /**
344: * Creates and returns a new instance of the Ant task mapped to the
345: * specified logical name using the
346: * {@link org.apache.cactus.integration.ant.util.AntTaskFactory} set.
347: *
348: * @param theName The logical name of the task to create
349: * @return A new isntance of the task
350: * @see AntTaskFactory#createTask
351: */
352: protected final Task createAntTask(String theName) {
353: return this .antTaskFactory.createTask(theName);
354: }
355:
356: /**
357: * Convenience method for creating a new directory inside another one.
358: *
359: * @param theParentDir The directory in which the new directory should be
360: * created
361: * @param theName The name of the directory to create
362: * @return The new directory
363: * @throws IOException If the directory could not be created
364: */
365: protected final File createDirectory(File theParentDir,
366: String theName) throws IOException {
367: File dir = new File(theParentDir, theName);
368: dir.mkdirs();
369: if (!dir.isDirectory()) {
370: throw new IOException("Couldn't create directory "
371: + dir.getAbsolutePath());
372: }
373: return dir;
374: }
375:
376: /**
377: * Creates the default filter chain that should be applied while copying
378: * container configuration files to the temporary directory from which the
379: * container is started. The default filter chain replaces all occurences
380: * of @cactus.port@ with the TCP port of the container, and all occurences
381: * of @cactus.context@ with the web-application's context path (if the
382: * deployable file is a web-app).
383: *
384: * @return The default filter chain
385: */
386: protected final FilterChain createFilterChain() {
387: ReplaceTokens.Token token = null;
388: FilterChain filterChain = new FilterChain();
389:
390: // Token for the cactus port
391: ReplaceTokens replacePort = new ReplaceTokens();
392: token = new ReplaceTokens.Token();
393: token.setKey("cactus.port");
394: token.setValue(String.valueOf(getPort()));
395: replacePort.addConfiguredToken(token);
396: filterChain.addReplaceTokens(replacePort);
397:
398: // Token for the cactus webapp context.
399: if (getDeployableFile() != null) {
400: ReplaceTokens replaceContext = new ReplaceTokens();
401: token = new ReplaceTokens.Token();
402: token.setKey("cactus.context");
403: token.setValue(getDeployableFile().getTestContext());
404: replaceContext.addConfiguredToken(token);
405: filterChain.addReplaceTokens(replaceContext);
406: }
407:
408: return filterChain;
409: }
410:
411: /**
412: * Clean the temporary directory.
413: *
414: * @param theTmpDir the temp directory to clean
415: */
416: protected void cleanTempDirectory(File theTmpDir) {
417: // Clean up stuff previously put in the temporary directory
418: Delete delete = (Delete) createAntTask("delete");
419: FileSet fileSet = new FileSet();
420: fileSet.setDir(theTmpDir);
421: fileSet.createInclude().setName("**/*");
422: delete.addFileset(fileSet);
423: delete.setIncludeEmptyDirs(true);
424: delete.setFailOnError(false);
425: delete.execute();
426: }
427:
428: /**
429: * Convenience method that creates a temporary directory or
430: * prepares the one passed by the user.
431: *
432: * @return The temporary directory
433: * @param theCustomTmpDir The user specified custom dir or null if none has
434: * been specified (ie we'll create default one).
435: * @param theName The name of the directory relative to the system specific
436: * temporary directory
437: */
438: protected File setupTempDirectory(File theCustomTmpDir,
439: String theName) {
440: File tmpDir;
441:
442: if (theCustomTmpDir == null) {
443: tmpDir = new File(System.getProperty("java.io.tmpdir"),
444: theName);
445: } else {
446: tmpDir = theCustomTmpDir;
447: }
448:
449: if (!tmpDir.exists()) {
450: if (!tmpDir.mkdirs()) {
451: throw new BuildException("Could not create temporary "
452: + "directory [" + tmpDir + "]");
453: }
454: }
455:
456: // make sure we're returning a directory
457: if (!tmpDir.isDirectory()) {
458: throw new BuildException("[" + tmpDir
459: + "] is not a directory");
460: }
461:
462: return tmpDir;
463: }
464:
465: /**
466: * Returns the log to use.
467: *
468: * @return The log
469: */
470: protected final Log getLog() {
471: return this .log;
472: }
473:
474: /**
475: * Returns the web-application archive that is to be deployed to the
476: * container.
477: *
478: * @return The WAR file
479: */
480: protected final DeployableFile getDeployableFile() {
481: return this .deployableFile;
482: }
483:
484: // Private Methods ---------------------------------------------------------
485:
486: /**
487: * Tests whether the property necessary to run the tests in the container
488: * has been set.
489: *
490: * @return <code>true</code> if the tests should be run in the container,
491: * <code>false</code> otherwise
492: */
493: private boolean testIfCondition() {
494: if (ifCondition == null || ifCondition.length() == 0) {
495: return true;
496: }
497:
498: return (getProject().getProperty(ifCondition) != null);
499: }
500:
501: /**
502: * Tests whether the property specified as the 'unless' condition has not
503: * been set.
504: *
505: * @return <code>true</code> if the tests should be run in the container,
506: * <code>false</code> otherwise
507: */
508: private boolean testUnlessCondition() {
509: if (unlessCondition == null || unlessCondition.length() == 0) {
510: return true;
511: }
512: return (getProject().getProperty(unlessCondition) == null);
513: }
514:
515: }
|