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