001: package org.netbeans.modules.web.core.test;
002:
003: /*
004: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
005: *
006: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
007: *
008: * The contents of this file are subject to the terms of either the GNU
009: * General Public License Version 2 only ("GPL") or the Common
010: * Development and Distribution License("CDDL") (collectively, the
011: * "License"). You may not use this file except in compliance with the
012: * License. You can obtain a copy of the License at
013: * http://www.netbeans.org/cddl-gplv2.html
014: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
015: * specific language governing permissions and limitations under the
016: * License. When distributing the software, include this License Header
017: * Notice in each file and include the License file at
018: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
019: * particular file as subject to the "Classpath" exception as provided
020: * by Sun in the GPL Version 2 section of the License file that
021: * accompanied this code. If applicable, add the following below the
022: * License Header, with the fields enclosed by brackets [] replaced by
023: * your own identifying information:
024: * "Portions Copyrighted [year] [name of copyright owner]"
025: *
026: * Contributor(s):
027: *
028: * The Original Software is NetBeans. The Initial Developer of the Original
029: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
030: * Microsystems, Inc. All Rights Reserved.
031: *
032: * If you wish your version of this file to be governed by only the CDDL
033: * or only the GPL Version 2, indicate your decision by adding
034: * "[Contributor] elects to include this software in this distribution
035: * under the [CDDL or GPL Version 2] license." If you do not indicate a
036: * single choice of license, a recipient has the option to distribute
037: * your version of this file under either the CDDL, the GPL Version 2 or
038: * to extend the choice of license to its licensees as provided above.
039: * However, if you add GPL Version 2 code and therefore, elected the GPL
040: * Version 2 license, then the option applies only if the new code is
041: * made subject to such option by the copyright holder.
042: */
043:
044: import java.beans.PropertyVetoException;
045: import java.io.BufferedInputStream;
046: import java.io.BufferedOutputStream;
047: import java.io.File;
048: import java.io.FileInputStream;
049: import java.io.FileOutputStream;
050: import java.io.IOException;
051: import java.io.InputStream;
052: import java.io.OutputStream;
053: import java.net.URL;
054: import java.util.ArrayList;
055: import java.util.List;
056: import java.util.Map;
057: import java.util.StringTokenizer;
058: import java.util.WeakHashMap;
059: import java.util.zip.ZipEntry;
060: import java.util.zip.ZipInputStream;
061: import junit.framework.Assert;
062: import org.netbeans.api.project.Project;
063: import org.netbeans.junit.NbTestCase;
064: import org.netbeans.modules.j2ee.deployment.impl.ServerRegistry;
065: import org.netbeans.modules.j2ee.deployment.plugins.api.InstanceProperties;
066: import org.netbeans.spi.project.ProjectFactory;
067: import org.netbeans.spi.project.ProjectState;
068: import org.netbeans.spi.project.support.ant.AntProjectHelper;
069: import org.netbeans.spi.project.support.ant.EditableProperties;
070: import org.openide.filesystems.FileLock;
071: import org.openide.filesystems.FileObject;
072: import org.openide.filesystems.FileSystem;
073: import org.openide.filesystems.FileUtil;
074: import org.openide.filesystems.LocalFileSystem;
075: import org.openide.filesystems.MultiFileSystem;
076: import org.openide.filesystems.Repository;
077: import org.openide.filesystems.URLMapper;
078: import org.openide.filesystems.XMLFileSystem;
079: import org.openide.modules.InstalledFileLocator;
080: import org.openide.util.Lookup;
081: import org.openide.util.lookup.Lookups;
082: import org.openide.util.lookup.ProxyLookup;
083: import org.xml.sax.SAXException;
084:
085: /**
086: * Help set up org.netbeans.api.project.*Test.
087: * @author Jesse Glick
088: */
089: public final class TestUtil extends ProxyLookup {
090:
091: static {
092: TestUtil.class.getClassLoader().setDefaultAssertionStatus(true);
093: System.setProperty("org.openide.util.Lookup", TestUtil.class
094: .getName());
095: Assert.assertEquals(TestUtil.class, Lookup.getDefault()
096: .getClass());
097: }
098:
099: private static TestUtil DEFAULT;
100: private static final int BUFFER = 2048;
101:
102: /** Do not call directly */
103: public TestUtil() {
104: Assert.assertNull(DEFAULT);
105: DEFAULT = this ;
106: setLookup(new Object[0]);
107: }
108:
109: /**
110: * Set the global default lookup.
111: * Caution: if you don't include Lookups.metaInfServices, you may have trouble,
112: * e.g. {@link #makeScratchDir} will not work.
113: */
114: public static void setLookup(Lookup l) {
115: DEFAULT.setLookups(new Lookup[] { l });
116: }
117:
118: /**
119: * Set the global default lookup with some fixed instances including META-INF/services/*.
120: */
121: public static void setLookup(Object[] instances) {
122: ClassLoader l = TestUtil.class.getClassLoader();
123: DEFAULT.setLookups(new Lookup[] { Lookups.fixed(instances),
124: Lookups.metaInfServices(l), Lookups.singleton(l), });
125: }
126:
127: private static boolean warned = false;
128:
129: /**
130: * Create a scratch directory for tests.
131: * Will be in /tmp or whatever, and will be empty.
132: * If you just need a java.io.File use clearWorkDir + getWorkDir.
133: */
134: public static FileObject makeScratchDir(NbTestCase test)
135: throws IOException {
136: test.clearWorkDir();
137: File root = test.getWorkDir();
138: assert root.isDirectory() && root.list().length == 0;
139: FileObject fo = FileUtil.toFileObject(root);
140: if (fo != null) {
141: return fo;
142: } else {
143: if (!warned) {
144: warned = true;
145: System.err
146: .println("No FileObject for "
147: + root
148: + " found.\n"
149: + "Maybe you need ${openide/masterfs.dir}/modules/org-netbeans-modules-masterfs.jar\n"
150: + "in test.unit.run.cp.extra, or make sure Lookups.metaInfServices is included in Lookup.default, so that\n"
151: + "Lookup.default<URLMapper>="
152: + Lookup.getDefault().lookup(
153: new Lookup.Template(
154: URLMapper.class))
155: .allInstances()
156: + " includes MasterURLMapper\n"
157: + "e.g. by using TestUtil.setLookup(Object[]) rather than TestUtil.setLookup(Lookup).");
158: }
159: // For the benefit of those not using masterfs.
160: LocalFileSystem lfs = new LocalFileSystem();
161: try {
162: lfs.setRootDirectory(root);
163: } catch (PropertyVetoException e) {
164: assert false : e;
165: }
166: Repository.getDefault().addFileSystem(lfs);
167: return lfs.getRoot();
168: }
169: }
170:
171: /**
172: * Delete a file and all subfiles.
173: */
174: public static void deleteRec(File f) throws IOException {
175: if (f.isDirectory()) {
176: File[] kids = f.listFiles();
177: if (kids == null) {
178: throw new IOException("List " + f);
179: }
180: for (int i = 0; i < kids.length; i++) {
181: deleteRec(kids[i]);
182: }
183: }
184: if (!f.delete()) {
185: throw new IOException("Delete " + f);
186: }
187: }
188:
189: /**
190: * Create a testing project factory which recognizes directories containing
191: * a subdirectory called "testproject".
192: * If that subdirectory contains a file named "broken" then loading the project
193: * will fail with an IOException.
194: */
195: public static ProjectFactory testProjectFactory() {
196: return new TestProjectFactory();
197: }
198:
199: /**
200: * Try to force a GC.
201: */
202: public static void gc() {
203: System.gc();
204: System.runFinalization();
205: System.gc();
206: }
207:
208: private static final Map<FileObject, Integer> loadCount = new WeakHashMap();
209:
210: /**
211: * Check how many times {@link ProjectFactory#loadProject} has been called
212: * (with any outcome) on a given project directory.
213: */
214: public static int projectLoadCount(FileObject dir) {
215: Integer i = (Integer) loadCount.get(dir);
216: if (i != null) {
217: return i.intValue();
218: } else {
219: return 0;
220: }
221: }
222:
223: /**
224: * Mark a test project to fail with a given error when it is next saved.
225: * The error only applies to the next save, not subsequent ones.
226: * @param p a test project
227: * @param error an error to throw (IOException or Error or RuntimeException),
228: * or null if it should succeed
229: */
230: public static void setProjectSaveWillFail(Project p, Throwable error) {
231: ((TestProject) p).error = error;
232: }
233:
234: /**
235: * Get the number of times a test project was successfully saved with no error.
236: * @param p a test project
237: * @return the save count
238: */
239: public static int projectSaveCount(Project p) {
240: return ((TestProject) p).saveCount;
241: }
242:
243: /**
244: * Mark a test project as modified.
245: * @param p a test project
246: */
247: public static void modify(Project p) {
248: ((TestProject) p).state.markModified();
249: }
250:
251: /**
252: * Mark a test project as modified.
253: * @param p a test project
254: */
255: public static void notifyDeleted(Project p) {
256: ((TestProject) p).state.notifyDeleted();
257: }
258:
259: /**
260: * Register Sun Application Server in the "IDE" to be used by unit test.
261: * This method creates dummy userdir as well as dummy NetBeans home
262: * in test's working directory. Both properties - <code>netbeans.home</code>
263: * and <code>netbeans.user</code> - will be set by this method if they are
264: * not already defined.
265: *
266: * @param test a test which requires SunAppServer
267: * @return id of registered server
268: */
269: public static String registerSunAppServer(NbTestCase test)
270: throws Exception {
271: return registerSunAppServer(test, new Object[0]);
272: }
273:
274: public static String registerSunAppServer(NbTestCase test,
275: Object[] additionalLookupItems) throws Exception {
276: String oldNbHome = System.getProperty("netbeans.home"); // NOI18N
277: String oldNbUser = System.getProperty("netbeans.user"); // NOI18N
278: File root = test.getWorkDir();
279: File systemDir = new File(root, "ud/system"); // NOI18N
280: new File(systemDir, "J2EE/InstalledServers").mkdirs(); // NOI18N
281: new File(systemDir, "J2EE/DeploymentPlugins").mkdirs(); // NOI18N
282: new File(root, "nb").mkdirs(); // NOI18N
283: System.setProperty("netbeans.home", new File(test.getWorkDir(),
284: "nb").getAbsolutePath()); // NOI18N
285: System.setProperty("netbeans.user", new File(test.getWorkDir(),
286: "ud").getAbsolutePath()); // NOI18N
287:
288: // lookup content
289: Object[] appServerNeed = new Object[] { new Repo(test),
290: new IFL() };
291: Object[] instances = new Object[additionalLookupItems.length
292: + appServerNeed.length];
293: System.arraycopy(additionalLookupItems, 0, instances, 0,
294: additionalLookupItems.length);
295: System.arraycopy(appServerNeed, 0, instances,
296: additionalLookupItems.length, appServerNeed.length);
297: TestUtil.setLookup(instances);
298:
299: File asRoot = null;
300: if (System.getProperty("appserv.home") != null) { // NOI18N
301: asRoot = new File(System.getProperty("appserv.home")); // NOI18N
302: } else {
303: asRoot = extractAppSrv(test.getWorkDir(), new File(test
304: .getDataDir(), "SunAppServer.zip")); // NOI18N
305: }
306: FileObject dir = Repository.getDefault().getDefaultFileSystem()
307: .findResource("/J2EE/InstalledServers"); // NOI18N
308: String name = FileUtil.findFreeFileName(dir, "instance", null); // NOI18N
309: FileObject instanceFO = dir.createData(name);
310: String serverID = "[" + asRoot.getAbsolutePath()
311: + "]deployer:Sun:AppServer::localhost:4848"; // NOI18N
312: instanceFO.setAttribute(InstanceProperties.URL_ATTR, serverID);
313: instanceFO.setAttribute(InstanceProperties.USERNAME_ATTR,
314: "admin"); // NOI18N
315: instanceFO.setAttribute(InstanceProperties.PASSWORD_ATTR,
316: "adminadmin"); // NOI18N
317: instanceFO.setAttribute(InstanceProperties.DISPLAY_NAME_ATTR,
318: "testdname"); // NOI18N
319: instanceFO.setAttribute(InstanceProperties.HTTP_PORT_NUMBER,
320: "4848"); // NOI18N
321: instanceFO.setAttribute("DOMAIN", "testdomain1"); // NOI18N
322: instanceFO.setAttribute("LOCATION", new File(asRoot, "domains")
323: .getAbsolutePath()); // NOI18N
324: ServerRegistry sr = ServerRegistry.getInstance();
325: sr.addInstance(instanceFO);
326: if (oldNbHome != null) {
327: System.setProperty("netbeans.home", oldNbHome); // NOI18N
328: }
329: if (oldNbUser != null) {
330: System.setProperty("netbeans.user", oldNbUser); // NOI18N
331: }
332: return serverID;
333: }
334:
335: private static File extractAppSrv(File destDir, File archiveFile)
336: throws IOException {
337: ZipInputStream zis = null;
338: BufferedOutputStream dest = null;
339: try {
340: FileInputStream fis = new FileInputStream(archiveFile);
341: zis = new ZipInputStream(new BufferedInputStream(fis));
342: ZipEntry entry;
343: while ((entry = zis.getNextEntry()) != null) {
344: byte data[] = new byte[BUFFER];
345: File entryFile = new File(destDir, entry.getName());
346: if (entry.isDirectory()) {
347: entryFile.mkdirs();
348: } else {
349: entryFile.getParentFile().mkdirs();
350: FileOutputStream fos = new FileOutputStream(
351: entryFile);
352: dest = new BufferedOutputStream(fos, BUFFER);
353: int count;
354: while ((count = zis.read(data, 0, BUFFER)) != -1) {
355: dest.write(data, 0, count);
356: }
357: dest.flush();
358: }
359: }
360: } finally {
361: if (zis != null) {
362: zis.close();
363: }
364: if (dest != null) {
365: dest.close();
366: }
367: }
368: return new File(destDir, archiveFile.getName().substring(0,
369: archiveFile.getName().length() - 4));
370: }
371:
372: public static EditableProperties loadProjectProperties(
373: final FileObject projectDir) throws IOException {
374: FileObject propsFO = projectDir
375: .getFileObject(AntProjectHelper.PROJECT_PROPERTIES_PATH);
376: InputStream propsIS = propsFO.getInputStream();
377: EditableProperties props = new EditableProperties(true);
378: try {
379: props.load(propsIS);
380: } finally {
381: propsIS.close();
382: }
383: return props;
384: }
385:
386: public static void storeProjectProperties(FileObject projectDir,
387: EditableProperties props) throws IOException {
388: FileObject propsFO = projectDir
389: .getFileObject(AntProjectHelper.PROJECT_PROPERTIES_PATH);
390: FileLock lock = propsFO.lock();
391: try {
392: OutputStream os = propsFO.getOutputStream(lock);
393: try {
394: props.store(os);
395: } finally {
396: os.close();
397: }
398: } finally {
399: lock.releaseLock();
400: }
401: }
402:
403: /**
404: * If set to something non-null, loading a broken project will wait for
405: * notification on this monitor before throwing an exception.
406: * @see ProjectManagerTest#testLoadExceptionWithConcurrentLoad
407: */
408: public static Object BROKEN_PROJECT_LOAD_LOCK = null;
409:
410: private static final class TestProjectFactory implements
411: ProjectFactory {
412:
413: TestProjectFactory() {
414: }
415:
416: public Project loadProject(FileObject projectDirectory,
417: ProjectState state) throws IOException {
418: Integer i = loadCount.get(projectDirectory);
419: if (i == null) {
420: i = 1;
421: } else {
422: i++;
423: }
424: loadCount.put(projectDirectory, i);
425: FileObject testproject = projectDirectory
426: .getFileObject("testproject");
427: if (testproject != null && testproject.isFolder()) {
428: if (testproject.getFileObject("broken") != null) {
429: if (BROKEN_PROJECT_LOAD_LOCK != null) {
430: synchronized (BROKEN_PROJECT_LOAD_LOCK) {
431: try {
432: BROKEN_PROJECT_LOAD_LOCK.wait();
433: } catch (InterruptedException e) {
434: assert false : e;
435: }
436: }
437: }
438: throw new IOException("Load failed of "
439: + projectDirectory);
440: } else {
441: return new TestProject(projectDirectory, state);
442: }
443: } else {
444: return null;
445: }
446: }
447:
448: public void saveProject(Project project) throws IOException,
449: ClassCastException {
450: TestProject p = (TestProject) project;
451: Throwable t = p.error;
452: if (t != null) {
453: p.error = null;
454: if (t instanceof IOException) {
455: throw (IOException) t;
456: } else if (t instanceof Error) {
457: throw (Error) t;
458: } else {
459: throw (RuntimeException) t;
460: }
461: }
462: p.saveCount++;
463: }
464:
465: public boolean isProject(FileObject dir) {
466: FileObject testproject = dir.getFileObject("testproject");
467: return testproject != null && testproject.isFolder();
468: }
469:
470: }
471:
472: private static final class TestProject implements Project {
473:
474: private final FileObject dir;
475: final ProjectState state;
476: Throwable error;
477: int saveCount = 0;
478:
479: public TestProject(FileObject dir, ProjectState state) {
480: this .dir = dir;
481: this .state = state;
482: }
483:
484: public Lookup getLookup() {
485: return Lookup.EMPTY;
486: }
487:
488: public FileObject getProjectDirectory() {
489: return dir;
490: }
491:
492: public String toString() {
493: return "testproject:" + getProjectDirectory().getNameExt();
494: }
495:
496: /* Probably unnecessary to have a ProjectInformation here:
497: public String getName() {
498: return "testproject:" + getProjectDirectory().getNameExt();
499: }
500:
501: public String getDisplayName() {
502: return "Test Project in " + getProjectDirectory().getNameExt();
503: }
504:
505: public Image getIcon() {
506: return null;
507: }
508:
509: public void addPropertyChangeListener(PropertyChangeListener listener) {}
510: public void removePropertyChangeListener(PropertyChangeListener listener) {}
511: */
512:
513: }
514:
515: /**
516: * Open a URL of content (for example from {@link Class#getResource}) and copy it to a named file.
517: * The new file can be given as a parent directory plus a relative (slash-separated) path.
518: * The file may not already exist, but intermediate directories may or may not.
519: * If the content URL is null, the file is just created, no more; if it already existed
520: * it is touched (timestamp updated) and its contents are cleared.
521: * @return the file object
522: */
523: public static FileObject createFileFromContent(URL content,
524: FileObject parent, String path) throws IOException {
525: if (parent == null) {
526: throw new IllegalArgumentException("null parent");
527: }
528: Assert.assertTrue("folder", parent.isFolder());
529: FileObject fo = parent;
530: StringTokenizer tok = new StringTokenizer(path, "/");
531: boolean touch = false;
532: while (tok.hasMoreTokens()) {
533: Assert.assertNotNull("fo is null (parent=" + parent
534: + " path=" + path + ")", fo);
535: String name = tok.nextToken();
536: if (tok.hasMoreTokens()) {
537: FileObject sub = fo.getFileObject(name);
538: if (sub == null) {
539: FileObject fo2 = fo.createFolder(name);
540: Assert.assertNotNull("createFolder(" + fo + ", "
541: + name + ") -> null", fo2);
542: fo = fo2;
543: } else {
544: Assert.assertTrue("folder", sub.isFolder());
545: fo = sub;
546: }
547: } else {
548: FileObject sub = fo.getFileObject(name);
549: if (sub == null) {
550: FileObject fo2 = fo.createData(name);
551: Assert.assertNotNull("createData(" + fo + ", "
552: + name + ") -> null", fo2);
553: fo = fo2;
554: } else {
555: fo = sub;
556: touch = true;
557: }
558: }
559: }
560: assert fo.isData();
561: if (content != null || touch) {
562: FileLock lock = fo.lock();
563: try {
564: OutputStream os = fo.getOutputStream(lock);
565: try {
566: if (content != null) {
567: InputStream is = content.openStream();
568: try {
569: FileUtil.copy(is, os);
570: } finally {
571: is.close();
572: }
573: }
574: } finally {
575: os.close();
576: }
577: } finally {
578: lock.releaseLock();
579: }
580: }
581: return fo;
582: }
583:
584: private static final class Repo extends Repository {
585:
586: public Repo(NbTestCase t) throws Exception {
587: super (mksystem(t));
588: }
589:
590: private static FileSystem mksystem(NbTestCase t)
591: throws Exception {
592: LocalFileSystem lfs = new LocalFileSystem();
593: File systemDir = new File(t.getWorkDir(), "ud/system");
594: systemDir.mkdirs();
595: lfs.setRootDirectory(systemDir);
596: lfs.setReadOnly(false);
597: List<FileSystem> layers = new ArrayList<FileSystem>();
598: layers.add(lfs);
599: /*
600: //get layer for the generic server
601: java.net.URL layerFile = Repo.class.getClassLoader().getResource("org/netbeans/modules/j2ee/genericserver/resources/layer.xml");
602: assert layerFile != null;
603: layers.add(new XMLFileSystem(layerFile));
604: */
605:
606: // get layer for the AS/GlassFish
607: // addLayer(layers, "org/netbeans/modules/j2ee/sun/ide/j2ee/layer.xml");
608: // addLayer(layers, "org/netbeans/modules/tomcat5/resources/layer.xml");
609: MultiFileSystem mfs = new MultiFileSystem(
610: (FileSystem[]) layers.toArray(new FileSystem[layers
611: .size()]));
612: return mfs;
613: }
614:
615: private static void addLayer(List<FileSystem> layers,
616: String layerRes) throws SAXException {
617: URL layerFile = Repo.class.getClassLoader().getResource(
618: layerRes);
619: assert layerFile != null;
620: layers.add(new XMLFileSystem(layerFile));
621: }
622:
623: }
624:
625: /** Copied from AntLoggerTest. */
626: private static final class IFL extends InstalledFileLocator {
627:
628: public IFL() {
629: }
630:
631: public File locate(String relativePath, String codeNameBase,
632: boolean localized) {
633: if (relativePath.equals("modules/ext/appsrvbridge.jar")) {
634: String path = System
635: .getProperty("test.appsrvbridge.jar");
636: Assert.assertNotNull("must set test.appsrvbridge.jar",
637: path);
638: return new File(path);
639: }
640: return null;
641: }
642: }
643:
644: }
|