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.web.project;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.beans.PropertyChangeSupport;
047: import java.io.File;
048: import java.io.IOException;
049: import java.util.*;
050: import java.util.logging.Level;
051: import java.util.logging.Logger;
052: import javax.swing.SwingUtilities;
053: import org.netbeans.api.java.classpath.ClassPath;
054: import org.netbeans.api.java.project.JavaProjectConstants;
055: import org.netbeans.api.project.Project;
056: import org.netbeans.api.project.ProjectUtils;
057: import org.netbeans.api.project.SourceGroup;
058: import org.netbeans.api.project.Sources;
059: import org.netbeans.api.project.ui.OpenProjects;
060: import org.netbeans.modules.j2ee.dd.api.web.DDProvider;
061: import org.netbeans.modules.j2ee.dd.api.web.WebApp;
062: import org.netbeans.modules.j2ee.dd.api.web.WebAppMetadata;
063: import org.netbeans.modules.j2ee.deployment.common.api.ConfigurationException;
064: import org.netbeans.modules.j2ee.deployment.devmodules.spi.J2eeModuleFactory;
065: import org.netbeans.modules.j2ee.deployment.devmodules.spi.J2eeModuleImplementation;
066: import org.netbeans.modules.j2ee.deployment.devmodules.spi.J2eeModuleProvider;
067: import org.netbeans.modules.j2ee.deployment.common.api.EjbChangeDescriptor;
068: import org.netbeans.modules.web.project.classpath.ClassPathProviderImpl;
069: import org.netbeans.spi.project.support.ant.AntProjectHelper;
070: import org.netbeans.spi.project.support.ant.EditableProperties;
071: import org.openide.filesystems.FileObject;
072: import org.netbeans.modules.j2ee.deployment.devmodules.api.*;
073: import org.netbeans.modules.web.project.ui.customizer.WebProjectProperties;
074: import org.netbeans.spi.java.classpath.ClassPathProvider;
075: import org.netbeans.modules.web.spi.webmodule.WebModuleImplementation;
076: import org.openide.filesystems.FileUtil;
077: import org.openide.NotifyDescriptor;
078: import org.openide.DialogDisplayer;
079: import org.openide.util.NbBundle;
080: import org.netbeans.modules.j2ee.dd.api.webservices.*;
081: import org.netbeans.modules.j2ee.dd.spi.MetadataUnit;
082: import org.netbeans.modules.j2ee.dd.spi.web.WebAppMetadataModelFactory;
083: import org.netbeans.modules.j2ee.dd.spi.webservices.WebservicesMetadataModelFactory;
084: import org.netbeans.modules.j2ee.metadata.model.api.MetadataModel;
085: import org.netbeans.modules.java.api.common.ant.UpdateHelper;
086: import org.netbeans.modules.websvc.spi.webservices.WebServicesConstants;
087:
088: /** A web module implementation on top of project.
089: *
090: * @author Pavel Buzek
091: */
092: public final class ProjectWebModule extends J2eeModuleProvider
093: implements WebModuleImplementation, J2eeModuleImplementation,
094: ModuleChangeReporter, EjbChangeDescriptor,
095: PropertyChangeListener {
096:
097: public static final String FOLDER_WEB_INF = "WEB-INF";//NOI18N
098: // public static final String FOLDER_CLASSES = "classes";//NOI18N
099: // public static final String FOLDER_LIB = "lib";//NOI18N
100: public static final String FILE_DD = "web.xml";//NOI18N
101:
102: private WebProject project;
103: private UpdateHelper helper;
104: private ClassPathProviderImpl cpProvider;
105: private String fakeServerInstId = null; // used to get access to properties of other servers
106:
107: private long notificationTimeout = 0; // used to suppress repeating the same messages
108:
109: private MetadataModel<WebAppMetadata> webAppMetadataModel;
110: private MetadataModel<WebAppMetadata> webAppAnnMetadataModel;
111: private MetadataModel<WebservicesMetadata> webservicesMetadataModel;
112:
113: private PropertyChangeSupport propertyChangeSupport;
114: private boolean webAppPropChangeLInitialized;
115:
116: private J2eeModule j2eeModule;
117:
118: ProjectWebModule(WebProject project, UpdateHelper helper,
119: ClassPathProviderImpl cpProvider) {
120: this .project = project;
121: this .helper = helper;
122: this .cpProvider = cpProvider;
123: project.evaluator().addPropertyChangeListener(this );
124: }
125:
126: public FileObject getDeploymentDescriptor() {
127: return getDeploymentDescriptor(false);
128: }
129:
130: public FileObject getDeploymentDescriptor(boolean silent) {
131: FileObject webInfFo = getWebInf(silent);
132: if (webInfFo == null) {
133: return null;
134: }
135: FileObject dd = webInfFo.getFileObject(FILE_DD);
136: if (dd == null
137: && !silent
138: && (J2eeModule.J2EE_13.equals(getJ2eePlatformVersion()) || J2eeModule.J2EE_14
139: .equals(getJ2eePlatformVersion()))) {
140: showErrorMessage(NbBundle.getMessage(
141: ProjectWebModule.class, "MSG_WebXmlNotFound", //NOI18N
142: webInfFo.getPath()));
143: }
144: return dd;
145: }
146:
147: public String getContextPath() {
148: if (getDeploymentDescriptor() == null) {
149: return null;
150: }
151: try {
152: return getConfigSupport().getWebContextRoot();
153: } catch (ConfigurationException e) {
154: // TODO #95280: inform the user that the context root cannot be retrieved
155: return null;
156: }
157: }
158:
159: public void setContextPath(String path) {
160: if (getDeploymentDescriptor() != null) {
161: try {
162: getConfigSupport().setWebContextRoot(path);
163: } catch (ConfigurationException e) {
164: // TODO #95280: inform the user that the context root cannot be set
165: }
166: }
167: }
168:
169: public String getContextPath(String serverInstId) {
170: fakeServerInstId = serverInstId;
171: String result = getContextPath();
172: fakeServerInstId = null;
173: return result;
174: }
175:
176: public void setContextPath(String serverInstId, String path) {
177: fakeServerInstId = serverInstId;
178: setContextPath(path);
179: fakeServerInstId = null;
180: }
181:
182: private void showErrorMessage(final String message) {
183: synchronized (this ) {
184: if (new Date().getTime() > notificationTimeout
185: && isProjectOpened()) {
186: // set timeout to suppress the same messages during next 20 seconds (feel free to adjust the timeout
187: // using more suitable value)
188: notificationTimeout = new Date().getTime() + 20000;
189: } else {
190: return;
191: }
192: }
193: // DialogDisplayer waits for the AWT thread, blocking the calling
194: // thread -- deadlock-prone, see issue #64888. therefore invoking
195: // only in the AWT thread
196: Runnable r = new Runnable() {
197: public void run() {
198: if (!SwingUtilities.isEventDispatchThread()) {
199: SwingUtilities.invokeLater(this );
200: } else {
201: DialogDisplayer.getDefault().notify(
202: new NotifyDescriptor.Message(message,
203: NotifyDescriptor.ERROR_MESSAGE));
204: }
205: }
206: };
207: r.run();
208: }
209:
210: public FileObject getDocumentBase() {
211: return getDocumentBase(false);
212: }
213:
214: public FileObject getDocumentBase(boolean silent) {
215: FileObject docBase = getFileObject(WebProjectProperties.WEB_DOCBASE_DIR);
216: if (docBase == null && !silent) {
217: String relativePath = helper.getAntProjectHelper()
218: .getStandardPropertyEvaluator().getProperty(
219: WebProjectProperties.WEB_DOCBASE_DIR);
220: String path = (relativePath != null ? helper
221: .getAntProjectHelper().resolvePath(relativePath)
222: : null);
223: String errorMessage;
224: if (path != null) {
225: errorMessage = NbBundle.getMessage(
226: ProjectWebModule.class,
227: "MSG_DocBase_Corrupted", project.getName(),
228: path);
229: } else {
230: errorMessage = NbBundle.getMessage(
231: ProjectWebModule.class,
232: "MSG_DocBase_Corrupted_Unknown", project
233: .getName());
234: }
235: showErrorMessage(errorMessage);
236: }
237: return docBase;
238: }
239:
240: @Deprecated
241: public FileObject[] getJavaSources() {
242: return project.getSourceRoots().getRoots();
243: }
244:
245: // public ClassPath getJavaSources () {
246: // ClassPathProvider cpp = (ClassPathProvider) project.getLookup ().lookup (ClassPathProvider.class);
247: // if (cpp != null) {
248: // return cpp.findClassPath (getFileObject ("src.dir"), ClassPath.SOURCE); //NOI18N
249: // }
250: // return null;
251: // }
252:
253: public FileObject getWebInf() {
254: return getWebInf(false);
255: }
256:
257: public FileObject getWebInf(boolean silent) {
258: FileObject webInf = getFileObject(WebProjectProperties.WEBINF_DIR);
259:
260: //temporary solution for < 6.0 projects
261: if (webInf == null) {
262: FileObject documentBase = getDocumentBase(silent);
263: if (documentBase == null) {
264: return null;
265: }
266: webInf = documentBase.getFileObject(FOLDER_WEB_INF);
267: }
268:
269: if (webInf == null && !silent) {
270: showErrorMessage(NbBundle.getMessage(
271: ProjectWebModule.class, "MSG_WebInfCorrupted2")); //NOI18N
272: }
273: return webInf;
274: }
275:
276: public FileObject getConfDir() {
277: return getFileObject(WebProjectProperties.CONF_DIR);
278: }
279:
280: public File getConfDirAsFile() {
281: return getFile(WebProjectProperties.CONF_DIR);
282: }
283:
284: public ClassPathProvider getClassPathProvider() {
285: return (ClassPathProvider) project.getLookup().lookup(
286: ClassPathProvider.class);
287: }
288:
289: public FileObject getArchive() {
290: return getFileObject("dist.war"); //NOI18N
291: }
292:
293: private FileObject getFileObject(String propname) {
294: String prop = helper.getAntProjectHelper()
295: .getStandardPropertyEvaluator().getProperty(propname);
296: if (prop != null) {
297: return helper.getAntProjectHelper().resolveFileObject(prop);
298: } else {
299: return null;
300: }
301: }
302:
303: private File getFile(String propname) {
304: String prop = helper.getAntProjectHelper()
305: .getStandardPropertyEvaluator().getProperty(propname);
306: if (prop != null) {
307: return helper.getAntProjectHelper().resolveFile(prop);
308: } else {
309: return null;
310: }
311: }
312:
313: public synchronized J2eeModule getJ2eeModule() {
314: if (j2eeModule == null) {
315: j2eeModule = J2eeModuleFactory.createJ2eeModule(this );
316: }
317: return j2eeModule;
318: }
319:
320: public org.netbeans.modules.j2ee.deployment.devmodules.api.ModuleChangeReporter getModuleChangeReporter() {
321: return this ;
322: }
323:
324: public File getDeploymentConfigurationFile(String name) {
325: assert name != null : "File name of the deployement configuration file can't be null"; //NOI18N
326:
327: String path = getConfigSupport().getContentRelativePath(name);
328: if (path == null) {
329: path = name;
330: }
331:
332: if (path.startsWith("WEB-INF/")) { //NOI18N
333: path = path.substring(8); //removing "WEB-INF/"
334:
335: FileObject webInf = getWebInf();
336: if (webInf == null) {
337: //in case that docbase is null ... but normally it should not be
338: return new File(getConfDirAsFile(), name);
339: }
340: return new File(FileUtil.toFile(webInf), path);
341: } else {
342: FileObject documentBase = getDocumentBase();
343: if (documentBase == null) {
344: //in case that docbase is null ... but normally it should not be
345: return new File(getConfDirAsFile(), name);
346: }
347: return new File(FileUtil.toFile(documentBase), path);
348: }
349: }
350:
351: public FileObject getModuleFolder() {
352: return getDocumentBase();
353: }
354:
355: public boolean useDefaultServer() {
356: return false;
357: }
358:
359: public String getServerID() {
360: String inst = getServerInstanceID();
361: if (inst != null) {
362: String id = Deployment.getDefault().getServerID(inst);
363: if (id != null) {
364: return id;
365: }
366: }
367: return helper.getAntProjectHelper()
368: .getStandardPropertyEvaluator().getProperty(
369: WebProjectProperties.J2EE_SERVER_TYPE);
370: }
371:
372: public String getServerInstanceID() {
373: if (fakeServerInstId != null)
374: return fakeServerInstId;
375: return helper.getAntProjectHelper()
376: .getStandardPropertyEvaluator().getProperty(
377: WebProjectProperties.J2EE_SERVER_INSTANCE);
378: }
379:
380: public void setServerInstanceID(String severInstanceID) {
381: WebProjectProperties.setServerInstance(project, helper,
382: severInstanceID);
383: }
384:
385: public Iterator getArchiveContents() throws java.io.IOException {
386: return new IT(getContentDirectory());
387: }
388:
389: public FileObject getContentDirectory() {
390: return getFileObject("build.web.dir"); //NOI18N
391: }
392:
393: public FileObject getBuildDirectory() {
394: return getFileObject("build.dir"); //NOI18N
395: }
396:
397: public File getContentDirectoryAsFile() {
398: return getFile("build.web.dir"); //NOI18N
399: }
400:
401: public <T> MetadataModel<T> getMetadataModel(Class<T> type) {
402: if (type == WebAppMetadata.class) {
403: @SuppressWarnings("unchecked")
404: // NOI18N
405: MetadataModel<T> model = (MetadataModel<T>) getAnnotationMetadataModel();
406: return model;
407: } else if (type == WebservicesMetadata.class) {
408: @SuppressWarnings("unchecked")
409: // NOI18N
410: MetadataModel<T> model = (MetadataModel<T>) getWebservicesMetadataModel();
411: return model;
412: }
413: return null;
414: }
415:
416: public synchronized MetadataModel<WebAppMetadata> getMetadataModel() {
417: if (webAppMetadataModel == null) {
418: FileObject ddFO = getDeploymentDescriptor();
419: File ddFile = ddFO != null ? FileUtil.toFile(ddFO) : null;
420: MetadataUnit metadataUnit = MetadataUnit
421: .create(
422: cpProvider
423: .getProjectSourcesClassPath(ClassPath.BOOT),
424: cpProvider
425: .getProjectSourcesClassPath(ClassPath.COMPILE),
426: cpProvider
427: .getProjectSourcesClassPath(ClassPath.SOURCE),
428: // XXX: add listening on deplymentDescriptor
429: ddFile);
430: webAppMetadataModel = WebAppMetadataModelFactory
431: .createMetadataModel(metadataUnit, true);
432: }
433: return webAppMetadataModel;
434: }
435:
436: /**
437: * The server plugin needs all models to be either merged on annotation-based.
438: * Currently only the web model does a bit of merging, other models don't. So
439: * for web we actually need two models (one for the server plugins and another
440: * for everyone else). Temporary solution until merging is implemented
441: * in all models.
442: */
443: public synchronized MetadataModel<WebAppMetadata> getAnnotationMetadataModel() {
444: if (webAppAnnMetadataModel == null) {
445: FileObject ddFO = getDeploymentDescriptor();
446: File ddFile = ddFO != null ? FileUtil.toFile(ddFO) : null;
447: MetadataUnit metadataUnit = MetadataUnit
448: .create(
449: cpProvider
450: .getProjectSourcesClassPath(ClassPath.BOOT),
451: cpProvider
452: .getProjectSourcesClassPath(ClassPath.COMPILE),
453: cpProvider
454: .getProjectSourcesClassPath(ClassPath.SOURCE),
455: // XXX: add listening on deplymentDescriptor
456: ddFile);
457: webAppAnnMetadataModel = WebAppMetadataModelFactory
458: .createMetadataModel(metadataUnit, false);
459: }
460: return webAppAnnMetadataModel;
461: }
462:
463: public synchronized MetadataModel<WebservicesMetadata> getWebservicesMetadataModel() {
464: if (webservicesMetadataModel == null) {
465: FileObject ddFO = getDD();
466: File ddFile = ddFO != null ? FileUtil.toFile(ddFO) : null;
467: MetadataUnit metadataUnit = MetadataUnit
468: .create(
469: cpProvider
470: .getProjectSourcesClassPath(ClassPath.BOOT),
471: cpProvider
472: .getProjectSourcesClassPath(ClassPath.COMPILE),
473: cpProvider
474: .getProjectSourcesClassPath(ClassPath.SOURCE),
475: // XXX: add listening on deplymentDescriptor
476: ddFile);
477: webservicesMetadataModel = WebservicesMetadataModelFactory
478: .createMetadataModel(metadataUnit);
479: }
480: return webservicesMetadataModel;
481: }
482:
483: public void uncacheDescriptors() {
484: // this.getConfigSupport().resetStorage();
485: // reset timeout when closing the project
486: notificationTimeout = 0;
487: }
488:
489: // TODO MetadataModel: rewrite when MetadataModel is ready
490: // private Webservices getWebservices() {
491: // if (Util.isJavaEE5orHigher(project)) {
492: // WebServicesSupport wss = WebServicesSupport.getWebServicesSupport(project.getProjectDirectory());
493: // try {
494: // return org.netbeans.modules.j2ee.dd.api.webservices.DDProvider.getDefault().getMergedDDRoot(wss);
495: // } catch (IOException ex) {
496: // ErrorManager.getDefault().notify(ex);
497: // }
498: // } else {
499: // FileObject wsdd = getDD();
500: // if(wsdd != null) {
501: // try {
502: // return org.netbeans.modules.j2ee.dd.api.webservices.DDProvider.getDefault()
503: // .getDDRoot(getDD());
504: // } catch (java.io.IOException e) {
505: // org.openide.ErrorManager.getDefault().log(e.getLocalizedMessage());
506: // }
507: // }
508: // }
509: // return null;
510: // }
511:
512: public org.netbeans.modules.j2ee.deployment.common.api.EjbChangeDescriptor getEjbChanges(
513: long timestamp) {
514: return this ;
515: }
516:
517: public Object getModuleType() {
518: return J2eeModule.WAR;
519: }
520:
521: public String getModuleVersion() {
522: // we don't want to use MetadataModel here as it can block
523: String version = null;
524: try {
525: FileObject ddFO = getDeploymentDescriptor();
526: if (ddFO != null) {
527: WebApp webApp = DDProvider.getDefault().getDDRoot(ddFO);
528: version = webApp.getVersion();
529: }
530: } catch (IOException e) {
531: Logger.getLogger("global").log(Level.WARNING, null, e); // NOI18N
532: }
533: if (version == null) {
534: // XXX should return a version based on the Java EE version
535: version = WebApp.VERSION_2_5;
536: }
537: return version;
538: }
539:
540: public void propertyChange(PropertyChangeEvent evt) {
541: if (evt
542: .getPropertyName()
543: .equals(
544: org.netbeans.modules.j2ee.dd.api.web.WebApp.PROPERTY_VERSION)) {
545: String oldVersion = (String) evt.getOldValue();
546: String newVersion = (String) evt.getNewValue();
547: getPropertyChangeSupport().firePropertyChange(
548: J2eeModule.PROP_MODULE_VERSION, oldVersion,
549: newVersion);
550: } else if (evt.getPropertyName().equals(
551: WebProjectProperties.J2EE_SERVER_INSTANCE)) {
552: Deployment d = Deployment.getDefault();
553: String oldServerID = evt.getOldValue() == null ? null : d
554: .getServerID((String) evt.getOldValue());
555: String newServerID = evt.getNewValue() == null ? null : d
556: .getServerID((String) evt.getNewValue());
557: fireServerChange(oldServerID, newServerID);
558: } else if (WebProjectProperties.RESOURCE_DIR.equals(evt
559: .getPropertyName())) {
560: String oldValue = (String) evt.getOldValue();
561: String newValue = (String) evt.getNewValue();
562: getPropertyChangeSupport().firePropertyChange(
563: J2eeModule.PROP_RESOURCE_DIRECTORY,
564: oldValue == null ? null : new File(oldValue),
565: newValue == null ? null : new File(newValue));
566: }
567: }
568:
569: public String getUrl() {
570: EditableProperties ep = helper
571: .getProperties(AntProjectHelper.PROJECT_PROPERTIES_PATH);
572: String warName = ep.getProperty(WebProjectProperties.WAR_NAME);
573: return warName == null ? "" : ("/" + warName); //NOI18N
574: }
575:
576: public boolean isManifestChanged(long timestamp) {
577: return false;
578: }
579:
580: public void setUrl(String url) {
581: throw new UnsupportedOperationException(
582: "Cannot customize URL of web module"); //NOI18N
583: }
584:
585: public boolean ejbsChanged() {
586: return false;
587: }
588:
589: public String[] getChangedEjbs() {
590: return new String[] {};
591: }
592:
593: public String getJ2eePlatformVersion() {
594: return helper.getAntProjectHelper()
595: .getStandardPropertyEvaluator().getProperty(
596: WebProjectProperties.J2EE_PLATFORM);
597: }
598:
599: public FileObject getDD() {
600: FileObject webInfFo = getWebInf();
601: if (webInfFo == null) {
602: showErrorMessage(NbBundle.getMessage(
603: ProjectWebModule.class, "MSG_WebInfCorrupted"));
604: return null;
605: }
606: return getWebInf().getFileObject(
607: WebServicesConstants.WEBSERVICES_DD, "xml"); // NOI18N
608: }
609:
610: public FileObject[] getSourceRoots() {
611: Sources sources = ProjectUtils.getSources(project);
612: SourceGroup[] groups = sources
613: .getSourceGroups(JavaProjectConstants.SOURCES_TYPE_JAVA);
614:
615: List roots = new LinkedList();
616: FileObject documentBase = getDocumentBase();
617: if (documentBase != null)
618: roots.add(documentBase);
619:
620: for (int i = 0; i < groups.length; i++) {
621: roots.add(groups[i].getRootFolder());
622: }
623:
624: FileObject[] rootArray = new FileObject[roots.size()];
625: return (FileObject[]) roots.toArray(rootArray);
626: }
627:
628: private boolean isProjectOpened() {
629: // XXX workaround: OpenProjects.getDefault() can be null
630: // when called from ProjectOpenedHook.projectOpened() upon IDE startup
631: if (OpenProjects.getDefault() == null)
632: return true;
633:
634: Project[] projects = OpenProjects.getDefault()
635: .getOpenProjects();
636: for (int i = 0; i < projects.length; i++) {
637: if (projects[i].equals(project))
638: return true;
639: }
640: return false;
641: }
642:
643: public File getResourceDirectory() {
644: return getFile(WebProjectProperties.RESOURCE_DIR);
645: }
646:
647: public void addPropertyChangeListener(
648: PropertyChangeListener listener) {
649: synchronized (this ) {
650: // XXX need to listen on the module version
651: // if (!webAppPropChangeLInitialized) {
652: // try {
653: // project.getWebModule().getMetadataModel().runReadAction(new MetadataModelAction<WebAppMetadata, Void>() {
654: // public Void run(WebAppMetadata metadata) throws MetadataModelException, IOException {
655: // WebApp webApp = metadata.getRoot();
656: // PropertyChangeListener l = (PropertyChangeListener) WeakListeners.create(PropertyChangeListener.class, ProjectWebModule.this, webApp);
657: // webApp.addPropertyChangeListener(l);
658: // return null;
659: // }
660: // });
661: // webAppPropChangeLInitialized = true;
662: // } catch (MetadataModelException e) {
663: // // TODO MetadataModel: how should we handle this?
664: // } catch (IOException e) {
665: // // TODO MetadataModel: how should we handle this?
666: // }
667: // }
668: }
669: getPropertyChangeSupport().addPropertyChangeListener(listener);
670: }
671:
672: public synchronized void removePropertyChangeListener(
673: PropertyChangeListener listener) {
674: if (propertyChangeSupport == null) {
675: return;
676: }
677: propertyChangeSupport.removePropertyChangeListener(listener);
678: }
679:
680: private synchronized PropertyChangeSupport getPropertyChangeSupport() {
681: if (propertyChangeSupport == null) {
682: propertyChangeSupport = new PropertyChangeSupport(this );
683: }
684: return propertyChangeSupport;
685: }
686:
687: private static class IT implements Iterator {
688: ArrayList ch;
689: FileObject root;
690:
691: private IT(FileObject f) {
692: this .ch = new ArrayList();
693: ch.add(f);
694: this .root = f;
695: }
696:
697: public boolean hasNext() {
698: return !ch.isEmpty();
699: }
700:
701: public Object next() {
702: FileObject f = (FileObject) ch.get(0);
703: ch.remove(0);
704: if (f.isFolder()) {
705: f.refresh();
706: FileObject chArr[] = f.getChildren();
707: for (int i = 0; i < chArr.length; i++) {
708: ch.add(chArr[i]);
709: }
710: }
711: return new FSRootRE(root, f);
712: }
713:
714: public void remove() {
715: throw new UnsupportedOperationException();
716: }
717:
718: }
719:
720: private static final class FSRootRE implements
721: J2eeModule.RootedEntry {
722: FileObject f;
723: FileObject root;
724:
725: FSRootRE(FileObject root, FileObject f) {
726: this .f = f;
727: this .root = root;
728: }
729:
730: public FileObject getFileObject() {
731: return f;
732: }
733:
734: public String getRelativePath() {
735: return FileUtil.getRelativePath(root, f);
736: }
737: }
738: }
|