0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package org.netbeans.modules.visualweb.insync;
0042:
0043: import java.beans.PropertyChangeEvent;
0044: import java.beans.PropertyChangeListener;
0045: import java.io.File;
0046: import java.io.IOException;
0047: import java.lang.reflect.Constructor;
0048: import java.net.MalformedURLException;
0049: import java.net.URL;
0050: import java.net.URLClassLoader;
0051: import java.util.ArrayList;
0052: import java.util.Arrays;
0053: import java.util.Collection;
0054: import java.util.Collections;
0055: import java.util.Enumeration;
0056: import java.util.HashMap;
0057: import java.util.HashSet;
0058: import java.util.IdentityHashMap;
0059: import java.util.Iterator;
0060: import java.util.LinkedHashSet;
0061: import java.util.List;
0062: import java.util.Map;
0063: import java.util.Properties;
0064: import java.util.Set;
0065: import java.util.jar.JarFile;
0066:
0067: import java.util.logging.Level;
0068: import java.util.logging.LogRecord;
0069: import java.util.logging.Logger;
0070: import javax.swing.SwingUtilities;
0071:
0072: import org.netbeans.api.java.classpath.ClassPath;
0073: import org.netbeans.api.project.FileOwnerQuery;
0074: import org.netbeans.api.project.Project;
0075: import org.netbeans.api.project.libraries.Library;
0076: import org.netbeans.api.project.libraries.LibraryManager;
0077: import org.netbeans.api.project.ui.OpenProjects;
0078: import org.netbeans.api.queries.SharabilityQuery;
0079: import org.netbeans.modules.j2ee.deployment.devmodules.api.Deployment;
0080: import org.netbeans.modules.j2ee.deployment.devmodules.api.J2eeModule;
0081: import org.netbeans.modules.j2ee.deployment.devmodules.api.J2eePlatform;
0082: import org.netbeans.modules.visualweb.classloaderprovider.CommonClassloaderProvider;
0083: import org.netbeans.modules.visualweb.extension.openide.util.Trace;
0084: import org.netbeans.modules.visualweb.project.jsf.api.JsfProjectUtils;
0085: import org.openide.ErrorManager;
0086: import org.openide.filesystems.FileAttributeEvent;
0087: import org.openide.filesystems.FileChangeListener;
0088: import org.openide.filesystems.FileEvent;
0089: import org.openide.filesystems.FileObject;
0090: import org.openide.filesystems.FileRenameEvent;
0091: import org.openide.filesystems.FileStateInvalidException;
0092: import org.openide.filesystems.FileSystem;
0093: import org.openide.filesystems.FileUtil;
0094: import org.openide.filesystems.URLMapper;
0095: import org.openide.loaders.DataLoaderPool;
0096: import org.openide.loaders.OperationEvent;
0097: import org.openide.loaders.OperationListener;
0098: import org.openide.util.Lookup;
0099: import org.openide.util.NbBundle;
0100: import org.openide.util.WeakListeners;
0101: import org.openide.util.Lookup.Result;
0102:
0103: //NB60 import org.netbeans.modules.visualweb.insync.faces.refactoring.MdrInSyncSynchronizer;
0104:
0105: /**
0106: * A ModelSet is a collection of Models that are organized together in a single project. The
0107: * ModelSet serves to coordinate the Models with each other and with the containing project. <p/>
0108: * The Models are divided into two groups: regular or source Models with many instances, and
0109: * specific configuration Models with one instance per type. <p/>There is always exactly one
0110: * ModelSet per project. <p/>
0111: *
0112: * @author cquinn
0113: */
0114: public abstract class ModelSet implements FileChangeListener {
0115:
0116: protected static class OpenProjectsListener implements
0117: PropertyChangeListener {
0118:
0119: public void propertyChange(PropertyChangeEvent event) {
0120: // The list of open projects has changed, clean up any old projects I may be holding
0121: // on to.
0122: // TODO: Should we look for opened ones as well, dont think so, better to create it
0123: // when needed ?
0124: if (OpenProjects.PROPERTY_OPEN_PROJECTS.equals(event
0125: .getPropertyName())) {
0126: Project[] openProjectsArray = OpenProjects.getDefault()
0127: .getOpenProjects();
0128: IdentityHashMap openProjects = new IdentityHashMap();
0129: for (int i = 0; i < openProjectsArray.length; i++)
0130: openProjects.put(openProjectsArray[i],
0131: openProjectsArray[i]);
0132: ArrayList toRemove = new ArrayList();
0133: synchronized (sets) {
0134: for (Iterator i = sets.keySet().iterator(); i
0135: .hasNext();) {
0136: Project project = (Project) i.next();
0137: if (!openProjects.containsKey(project)) {
0138: ModelSet modelSet = (ModelSet) sets
0139: .get(project);
0140: toRemove.add(modelSet);
0141: }
0142: }
0143: }
0144: for (Iterator i = toRemove.iterator(); i.hasNext();) {
0145: ModelSet modelSet = (ModelSet) i.next();
0146: modelSet.destroy();
0147: fireModelSetRemoved(modelSet);
0148: }
0149: }
0150: }
0151:
0152: }
0153:
0154: // The following code was cloned from WriteLockUtils.hasActiveLockFileSigns
0155: // When NB fixes ISSUE #59514, we can get rid of this code
0156: static final String WriteLock_PREFIX = ".LCK"; //NOI18N
0157: static final String WriteLock_SUFFIX = "~"; //NOI18N
0158:
0159: public static boolean hasActiveLockFileSigns(FileObject fileObject) {
0160: String name = fileObject.getNameExt();
0161: boolean hasSigns = name.endsWith(WriteLock_SUFFIX)
0162: && name.startsWith(WriteLock_PREFIX);
0163: return hasSigns;
0164: }
0165:
0166: static {
0167: // WindowManager.getDefault().getRegistry().addPropertyChangeListener(new WindowManagerPropertyRegistry());
0168: OpenProjects.getDefault().addPropertyChangeListener(
0169: new OpenProjectsListener());
0170: }
0171:
0172: protected static ArrayList modelSetsListeners = new ArrayList();
0173:
0174: public static void addModelSetsListener(ModelSetsListener listener) {
0175: modelSetsListeners.add(listener);
0176: }
0177:
0178: public static void removeModelSetsListener(
0179: ModelSetsListener listener) {
0180: modelSetsListeners.remove(listener);
0181: }
0182:
0183: protected static void fireModelSetAdded(ModelSet modelSet) {
0184: // !EAT
0185: // How to have an iteration safe collection ?
0186: // I think there may be a better way to do dependencies ?
0187: Object[] listeners = modelSetsListeners.toArray();
0188: for (int i = 0; i < listeners.length; i++) {
0189: ModelSetsListener listener = (ModelSetsListener) listeners[i];
0190: listener.modelSetAdded(modelSet);
0191: }
0192: }
0193:
0194: protected static void fireModelSetRemoved(ModelSet modelSet) {
0195: // !EAT
0196: // How to have an iteration safe collection ?
0197: // I think there may be a better way to do dependencies ?
0198: Object[] listeners = modelSetsListeners.toArray();
0199: for (int i = 0; i < listeners.length; i++) {
0200: ModelSetsListener listener = (ModelSetsListener) listeners[i];
0201: listener.modelSetRemoved(modelSet);
0202: }
0203: }
0204:
0205: //------------------------------------------------------------------------------ Model Factories
0206:
0207: protected static IdentityHashMap sets = new IdentityHashMap();
0208:
0209: protected static java.util.Collection getFactories() {
0210: return Lookup.getDefault().lookup(
0211: new Lookup.Template(Model.Factory.class))
0212: .allInstances();
0213: }
0214:
0215: private static Map<Project, Runnable> projectToRunnable = new HashMap<Project, Runnable>();
0216:
0217: protected static ModelSet startModeling(final Project project,
0218: final Class ofType) {
0219: if (project == null)
0220: return null;
0221: ModelSet set = getModelSet(project);
0222: if (set != null) {
0223: return set;
0224: }
0225: synchronized (projectToRunnable) {
0226: Runnable modelingRunnable = projectToRunnable.get(project);
0227: if (modelingRunnable == null) {
0228: modelingRunnable = new Runnable() {
0229: public void run() {
0230: try {
0231: getInstance(project, ofType);
0232: } finally {
0233: synchronized (projectToRunnable) {
0234: projectToRunnable.remove(project);
0235: }
0236: }
0237: }
0238: };
0239: projectToRunnable.put(project, modelingRunnable);
0240: new Thread(modelingRunnable, "Loading ModelSet for "
0241: + project.getProjectDirectory().getName())
0242: .start(); // NOI18N
0243: }
0244: }
0245: return null;
0246: }
0247:
0248: protected static ModelSet getInstance(FileObject file, Class ofType) {
0249: Project project = FileOwnerQuery.getOwner(file);
0250: return getInstance(project, ofType);
0251: }
0252:
0253: /**
0254: * Helper method for sub-classes to be able to get access to a ModelSet of their own specific type.
0255: * @param project
0256: * @return
0257: */
0258: synchronized protected static ModelSet getInstance(
0259: final Project project, final Class ofType) {
0260: if (project == null)
0261: return null;
0262: ModelSet set = null;
0263: synchronized (sets) {
0264: set = (ModelSet) sets.get(project);
0265: }
0266: if (set == null && ofType != null) {
0267: set = createInstance(project, ofType);
0268: if (set != null) {
0269: synchronized (sets) {
0270: sets.put(project, set);
0271: }
0272: fireModelSetAdded(set);
0273: }
0274: }
0275:
0276: return set;
0277: }
0278:
0279: private static ModelSet createInstance(Project project, Class ofType) {
0280: try {
0281: Constructor constructor = ofType
0282: .getConstructor(new Class[] { Project.class });
0283: ModelSet set = (ModelSet) constructor
0284: .newInstance(new Object[] { project });
0285: set.setInitialized();
0286: return set;
0287: } catch (Exception e) {
0288: throw new RuntimeException(e);
0289: }
0290: }
0291:
0292: protected static ModelSet getModelSet(FileObject file) {
0293: return getModelSet(FileOwnerQuery.getOwner(file));
0294: }
0295:
0296: protected static ModelSet getModelSet(Project project) {
0297: ModelSet set = null;
0298: synchronized (sets) {
0299: set = (ModelSet) sets.get(project);
0300: }
0301: return set;
0302: }
0303:
0304: private boolean initialized;
0305:
0306: public boolean isInitialized() {
0307: return initialized;
0308: }
0309:
0310: public void setInitialized() {
0311: this .initialized = true;
0312: }
0313:
0314: //--------------------------------------------------------------------------------- Construction
0315:
0316: protected final ClassLoader parentClassLoader; // classloader to parent the project classloader to
0317: protected final Project project;
0318: protected URLClassLoader classLoader; // current derived project classloader
0319: protected ClassPath classPath; // needed since we add ourseleves as a dependent
0320: protected ClassPathListener classPathListener;
0321: protected FileSystem fileSystem;
0322: protected Model configModel = null; // config model
0323:
0324: protected final IdentityHashMap models = new IdentityHashMap(); // general models
0325: protected final IdentityHashMap modelSetListeners = new IdentityHashMap();
0326:
0327: private final OperationListener operationListener = new ModelSetOperationListener();
0328:
0329: /**
0330: * Construct a ModelSet for a given project.
0331: * <p>
0332: * This will throw a <code>RuntimeException</code> if a suitable Common ClassLoader Provider is not found.
0333: *
0334: * @param project The project that this ModelSet is to be associated with,
0335: */
0336: protected ModelSet(Project project) {
0337: this .project = project;
0338:
0339: CommonClassloaderProvider commonClassloaderProvider = null;
0340:
0341: Properties capabilities = new Properties();
0342: capabilities.put(CommonClassloaderProvider.J2EE_PLATFORM,
0343: JsfProjectUtils.getJ2eePlatformVersion(project));
0344: Result result = Lookup.getDefault().lookup(
0345: new Lookup.Template(CommonClassloaderProvider.class));
0346: for (Iterator iterator = result.allInstances().iterator(); iterator
0347: .hasNext();) {
0348: CommonClassloaderProvider aCommonClassloaderProvider = (CommonClassloaderProvider) iterator
0349: .next();
0350: if (aCommonClassloaderProvider.isCapableOf(capabilities)) {
0351: commonClassloaderProvider = aCommonClassloaderProvider;
0352: break;
0353: }
0354: }
0355:
0356: if (commonClassloaderProvider == null) {
0357: throw new RuntimeException(
0358: "No Common Classloader Provider found."); // TODO I18N
0359: }
0360:
0361: parentClassLoader = commonClassloaderProvider.getClassLoader();
0362:
0363: // Run thru all the items and create models for those which are ours
0364: if (project != null) {
0365: getProjectClassLoader();
0366: assert Trace.trace("insync.model",
0367: "MS.ModelSet ModelCreateVisitor visiting project items in "
0368: + Thread.currentThread());
0369: ModelCreateVisitor visitor = new ModelCreateVisitor();
0370: for (Iterator i = getSourceRoots().iterator(); i.hasNext();) {
0371: FileObject root = (FileObject) i.next();
0372: visitor.traverse(root);
0373: }
0374: try {
0375: fileSystem = project.getProjectDirectory()
0376: .getFileSystem();
0377: } catch (FileStateInvalidException e) {
0378: }
0379: if (fileSystem != null) {
0380: fileSystem.addFileChangeListener(this );
0381: }
0382: }
0383: // XXX NB issue #81746.
0384: DataLoaderPool.getDefault().addOperationListener(
0385: (OperationListener) WeakListeners.create(
0386: OperationListener.class, operationListener,
0387: DataLoaderPool.getDefault()));
0388: }
0389:
0390: protected List getSourceRoots() {
0391: ArrayList list = new ArrayList();
0392: FileObject root = JsfProjectUtils.getDocumentRoot(project);
0393: if (root != null)
0394: list.add(root);
0395: root = JsfProjectUtils.getPageBeanRoot(project);
0396: if (root != null)
0397: list.add(root);
0398: return list;
0399: }
0400:
0401: /**
0402: * Destroy this ModelSet and all its contained Models and release their resources. This ModelSet
0403: * and the contained Models must never be used after destroy is called.
0404: */
0405: public void destroy() {
0406: if (fileSystem != null) {
0407: fileSystem.removeFileChangeListener(this );
0408: }
0409: releaseProjectClassLoader();
0410: Model[] ms = getModels();
0411: models.clear();
0412: // Make sure that none of the models are valid, such that if outline or any other view
0413: // wishes to update, they will get an empty list of contexts
0414: // for (int i = 0; i < ms.length; i++)
0415: // ms[i].resetOwner();
0416: for (int i = 0; i < ms.length; i++)
0417: ms[i].destroy();
0418:
0419: configModel.destroy();
0420:
0421: synchronized (sets) {
0422: sets.remove(project);
0423: }
0424: }
0425:
0426: //------------------------------------------------------------------------------------ Accessors
0427:
0428: /**
0429: * Get the project that this ModelSet is associated with.
0430: *
0431: * @return The project that this ModelSet is associated with.
0432: */
0433: public Project getProject() {
0434: return project;
0435: }
0436:
0437: private static String[] volumeTypes = new String[] { "classpath",
0438: "visual-web-designtime", };
0439:
0440: private static final String WEBSERVICE_CLIENTS_SUB_DIR = "webservice_clients"; // NOI18N
0441: private static final String EJB_DATA_SUB_DIR = "ejb-sources"; // NOI18N
0442:
0443: /**
0444: * Get the per-project class loader for this ModelSet. The class loader may change as project
0445: * settings and libraries are changed by the user.
0446: *
0447: * @return The class loader.
0448: */
0449: public URLClassLoader getProjectClassLoader() {
0450: if (classLoader == null) {
0451: Set<URL> urls1Set = new LinkedHashSet<URL>();
0452:
0453: // Add design time and run time jars from COMPLIBS
0454: LibraryManager libraryManager = JsfProjectUtils
0455: .getProjectLibraryManager(project);
0456: Library jaxrpc16Library = null;
0457: Library[] libraries = libraryManager.getLibraries();
0458: for (int i = 0; i < libraries.length; i++) {
0459: Library library = libraries[i];
0460:
0461: if (library.getType().equals("j2se")) {
0462: if (library.getName().equals("jaxrpc16")) {
0463: // cache
0464: jaxrpc16Library = library;
0465: continue;
0466: }
0467: }
0468:
0469: // TODO The following hardcoded constants are defined in
0470: // org.netbeans.modules.visualweb.project.jsf.libraries.provider.ComponentLibraryTypeProvider
0471: // org.netbeans.modules.visualweb.project.jsf.libraries.provider.ThemeLibraryTypeProvider
0472: // However this class is not part of a public package.
0473: if (library.getType().equals("complib")
0474: || library.getType().equals("theme")) { // NOI18N
0475: if (JsfProjectUtils.hasLibraryReference(project,
0476: library, ClassPath.COMPILE)) {
0477: for (String volumeType : volumeTypes) {
0478: if (library.getType().equals("theme") && // NOI18N
0479: (!volumeType.equals("classpath"))) { // NOI18N
0480: continue;
0481: }
0482: addEntriesInLibraryVolume(library,
0483: volumeType, urls1Set);
0484: }
0485: }
0486: }
0487: }
0488:
0489: // !EAT TODO: Is this really the correct way to build the class loader ???
0490: FileObject pageBeanRoot = JsfProjectUtils
0491: .getPageBeanRoot(project);
0492: classPath = ClassPath.getClassPath(pageBeanRoot,
0493: ClassPath.COMPILE);
0494:
0495: // Now scan and add all the files under
0496: // <project-lib-dir>/webservice_clients
0497: // <project-lib-dir>/ejb-sources
0498: boolean hasWebservicesClients = false;
0499: boolean hasEjbClients = false;
0500: FileObject projectLibDir;
0501: try {
0502: projectLibDir = JsfProjectUtils
0503: .getProjectLibraryDirectory(project);
0504: FileObject wsClientsSubDir = projectLibDir
0505: .getFileObject(WEBSERVICE_CLIENTS_SUB_DIR);
0506: if (wsClientsSubDir != null) {
0507: hasWebservicesClients = addJarsInFolder(
0508: wsClientsSubDir, urls1Set);
0509: }
0510: FileObject ejbClientsSubDir = projectLibDir
0511: .getFileObject(EJB_DATA_SUB_DIR);
0512: if (ejbClientsSubDir != null) {
0513: hasEjbClients = addJarsInFolder(ejbClientsSubDir,
0514: urls1Set);
0515: }
0516: } catch (IOException e) {
0517: // not found - ignore
0518: }
0519:
0520: // Special handling of jaxrpc16Library (J2EE 1.3 and J2EE 1.4)
0521: if (hasWebservicesClients) {
0522: String platformVersion = JsfProjectUtils
0523: .getJ2eePlatformVersion(project);
0524: if (platformVersion.equals(JsfProjectUtils.J2EE_1_3)
0525: || platformVersion
0526: .equals(JsfProjectUtils.J2EE_1_4)) {
0527: if (jaxrpc16Library == null) {
0528: // Hmmm...project does not have a reference to the jaxrpc16
0529: // library so we need to get it from global library manager
0530: Library[] globalLibraries = LibraryManager
0531: .getDefault().getLibraries();
0532: for (Library library : globalLibraries) {
0533: if (library.getType().equals("j2se")) {
0534: if (library.getName()
0535: .equals("jaxrpc16")) {
0536: // cache
0537: jaxrpc16Library = library;
0538: break;
0539: }
0540: }
0541: }
0542: }
0543: if (jaxrpc16Library != null) {
0544: // Add the jars from jaxrpc16Library
0545: addEntriesInLibraryVolume(jaxrpc16Library,
0546: "classpath", urls1Set); // NOI18N
0547: }
0548: }
0549: // For JAVA EE 5 projects the jaxws APIs are available thorugh a module dependency in
0550: // org.netbeans.modules.visualweb.j2ee15classloaderprovider
0551: // module
0552: }
0553:
0554: // Special handling of EJB
0555: if (hasEjbClients) {
0556: List<File> javaEEClasspathEntries = getJavaEEClasspathEntries();
0557: for (File javaEEClasspathEntry : javaEEClasspathEntries) {
0558: if (javaEEClasspathEntry.isFile()
0559: && javaEEClasspathEntry.getName().endsWith(
0560: ".jar")) {
0561: try {
0562: JarFile jarFile = new JarFile(
0563: javaEEClasspathEntry);
0564: // Found one of the ejb20 classes - use this jar file
0565: if (jarFile
0566: .getEntry("javax/ejb/CreateException.class") != null) {
0567: try {
0568: URL entry = javaEEClasspathEntry
0569: .toURI().toURL();
0570: if (FileUtil.isArchiveFile(entry)) {
0571: entry = FileUtil
0572: .getArchiveRoot(entry);
0573: }
0574: urls1Set.add(entry);
0575: break;
0576: } catch (MalformedURLException mue) {
0577: ErrorManager.getDefault().notify(
0578: ErrorManager.INFORMATIONAL,
0579: mue);
0580: }
0581: }
0582: } catch (IOException e) {
0583: // corrupt .jar file
0584: }
0585:
0586: }
0587: }
0588: }
0589:
0590: //Add <project>\build\web\WEB-INF\classes directory into project
0591: //classloader classpath.
0592: //TODO: We need to consider a better approach to achieve this
0593: //This may not be required if insync models all the source code
0594: FileObject docRoot = JsfProjectUtils
0595: .getDocumentRoot(project);
0596: File docPath = FileUtil.toFile(docRoot);
0597: File buildClassPath = new File(docPath.getParentFile(),
0598: "build" + File.separator + "web" + File.separator
0599: + "WEB-INF" + File.separator + "classes");
0600: URL buildClassURL = null;
0601: try {
0602: buildClassURL = buildClassPath.toURI().toURL();
0603: urls1Set.add(buildClassURL);
0604: } catch (MalformedURLException mue) {
0605: ErrorManager.getDefault().notify(
0606: ErrorManager.INFORMATIONAL, mue);
0607: }
0608:
0609: URL[] urls1 = (URL[]) urls1Set.toArray(new URL[0]);
0610:
0611: classLoader = new ProjectClassLoader(urls1,
0612: parentClassLoader);
0613: classPathListener = new ClassPathListener();
0614: classPath.addPropertyChangeListener(classPathListener);
0615: }
0616: return classLoader;
0617: }
0618:
0619: private static List<File> getJavaEEClasspathEntries() {
0620: for (String serverInstanceID : Deployment.getDefault()
0621: .getServerInstanceIDs()) {
0622: String displayName = Deployment.getDefault()
0623: .getServerInstanceDisplayName(serverInstanceID);
0624: J2eePlatform j2eePlatform = Deployment.getDefault()
0625: .getJ2eePlatform(serverInstanceID);
0626: if (displayName != null
0627: && j2eePlatform != null
0628: && j2eePlatform.getSupportedModuleTypes().contains(
0629: J2eeModule.EJB)) {
0630: File[] classpath = j2eePlatform.getClasspathEntries();
0631: return Arrays.asList(classpath);
0632: }
0633: }
0634: return Collections.emptyList();
0635: }
0636:
0637: private static void addEntriesInLibraryVolume(Library library,
0638: String volumeType, Set urlsSet) {
0639: List urls = library.getContent(volumeType);
0640: List normalizedUrls = new ArrayList();
0641:
0642: for (Iterator it = urls.iterator(); it.hasNext();) {
0643: URL url = (URL) it.next();
0644: FileObject fileObject = URLMapper.findFileObject(url);
0645:
0646: //file inside library is broken
0647: if (fileObject == null)
0648: continue;
0649:
0650: if ("jar".equals(url.getProtocol())) { //NOI18N
0651: fileObject = FileUtil.getArchiveFile(fileObject);
0652: }
0653: File f = FileUtil.toFile(fileObject);
0654: if (f != null) {
0655: try {
0656: URL entry = f.toURI().toURL();
0657: if (FileUtil.isArchiveFile(entry)) {
0658: entry = FileUtil.getArchiveRoot(entry);
0659: } else if (!f.exists()) {
0660: // if file does not exist (e.g. build/classes folder
0661: // was not created yet) then corresponding File will
0662: // not be ended with slash. Fix that.
0663: assert !entry.toExternalForm().endsWith("/") : f; // NOI18N
0664: entry = new URL(entry.toExternalForm() + "/"); // NOI18N
0665: }
0666: normalizedUrls.add(entry);
0667: } catch (MalformedURLException mue) {
0668: ErrorManager.getDefault().notify(
0669: ErrorManager.INFORMATIONAL, mue);
0670: }
0671: }
0672: }
0673: urlsSet.addAll(normalizedUrls); // NOI18N
0674: }
0675:
0676: private static boolean addJarsInFolder(FileObject folder,
0677: Set urlsSet) {
0678: boolean added = false;
0679: Enumeration<? extends FileObject> children = folder
0680: .getChildren(true);
0681: while (children.hasMoreElements()) {
0682: FileObject child = children.nextElement();
0683: if (child.isData() && child.getExt().equals("jar")) {
0684: File file = FileUtil.toFile(child);
0685: if (file != null) {
0686: try {
0687: URL entry = file.toURI().toURL();
0688: if (FileUtil.isArchiveFile(entry)) {
0689: entry = FileUtil.getArchiveRoot(entry);
0690: }
0691: urlsSet.add(entry);
0692: added = true;
0693: } catch (MalformedURLException mue) {
0694: ErrorManager.getDefault().notify(
0695: ErrorManager.INFORMATIONAL, mue);
0696: }
0697: }
0698: }
0699: }
0700: return added;
0701: }
0702:
0703: // XXX To be able to distinguish our specific project classloader during debugging.
0704: private static class ProjectClassLoader extends URLClassLoader {
0705: private final URL[] urls;
0706:
0707: // Memory leak probing
0708: private static final Logger TIMERS = Logger
0709: .getLogger("TIMER.visualweb"); // NOI18N
0710:
0711: public ProjectClassLoader(URL[] urls, ClassLoader parent) {
0712: super (urls, parent);
0713: this .urls = urls;
0714:
0715: if (TIMERS.isLoggable(Level.FINER)) {
0716: LogRecord rec = new LogRecord(Level.FINER,
0717: "ModelSet$ProjectClassLoader"); // NOI18N
0718: rec.setParameters(new Object[] { this });
0719: TIMERS.log(rec);
0720: }
0721: }
0722:
0723: public String toString() {
0724: return super .toString() + "[urls="
0725: + (urls == null ? null : Arrays.asList(urls)) + "]"; // NOI18N
0726: }
0727: }
0728:
0729: /**
0730: * Get an array of all of the source Models in this set.
0731: * @return An array of all of the source Models in this set.
0732: */
0733: public Model[] getModels() {
0734: return (Model[]) getModelsMap().values().toArray(
0735: Model.EMPTY_ARRAY);
0736: }
0737:
0738: protected Map getModelsMap() {
0739: return models;
0740: }
0741:
0742: /**
0743: * Get the corresponding source model for a NB file object.
0744: *
0745: * @param file The NB file object
0746: * @return The corresponding model.
0747: */
0748: public Model getModel(FileObject file) {
0749: Model model = (Model) getModelsMap().get(file);
0750: return model;
0751: }
0752:
0753: /**
0754: * Get the configuration model
0755: *
0756: * @return th configuration model.
0757: */
0758: public Model getConfigModel() {
0759: return configModel;
0760: }
0761:
0762: /**
0763: * Set the configuration model
0764: *
0765: * @return The configuration model.
0766: */
0767: public void setConfigModel(Model configModel) {
0768: this .configModel = configModel;
0769: }
0770:
0771: public void addModelSetListener(ModelSetListener listener) {
0772: modelSetListeners.put(listener, "");
0773: }
0774:
0775: public void removeModelSetListener(ModelSetListener listener) {
0776: modelSetListeners.remove(listener);
0777: }
0778:
0779: protected void fireModelAdded(Model model) {
0780: // !EAT
0781: // How to have an iteration safe collection ?
0782: // I think there may be a better way to do dependencies ?
0783: for (Iterator iterator = modelSetListeners.keySet().iterator(); iterator
0784: .hasNext();) {
0785: ModelSetListener listener = (ModelSetListener) iterator
0786: .next();
0787: listener.modelAdded(model);
0788: }
0789: }
0790:
0791: protected void fireModelChanged(Model model) {
0792: // !EAT
0793: // How to have an iteration safe collection ?
0794: // I think there may be a better way to do dependencies ?
0795: for (Iterator iterator = modelSetListeners.keySet().iterator(); iterator
0796: .hasNext();) {
0797: ModelSetListener listener = (ModelSetListener) iterator
0798: .next();
0799: listener.modelChanged(model);
0800: }
0801: }
0802:
0803: protected void fireModelProjectChanged() {
0804: // !EAT
0805: // How to have an iteration safe collection ?
0806: // I think there may be a better way to do dependencies ?
0807: for (Iterator iterator = modelSetListeners.keySet().iterator(); iterator
0808: .hasNext();) {
0809: ModelSetListener listener = (ModelSetListener) iterator
0810: .next();
0811: listener.modelProjectChanged();
0812: }
0813: }
0814:
0815: protected void fireModelRemoved(Model model) {
0816: // !EAT
0817: // How to have an iteration safe collection ?
0818: // I think there may be a better way to do dependencies ?
0819: for (Iterator iterator = modelSetListeners.keySet().iterator(); iterator
0820: .hasNext();) {
0821: ModelSetListener listener = (ModelSetListener) iterator
0822: .next();
0823: listener.modelRemoved(model);
0824: }
0825: }
0826:
0827: //---------------------------------------------------------------------------------------- Model
0828:
0829: /**
0830: * Add a new file/model pair to the correct map.
0831: * @param file The source or config file object
0832: * @param m The source or config model
0833: */
0834: protected void addModel(FileObject file, Model m) {
0835: models.put(file, m);
0836: fireModelAdded(m);
0837: }
0838:
0839: private Set modelsToSync;
0840:
0841: void addToModelsToSync(Model model) {
0842: if (modelsToSync == null) {
0843: modelsToSync = new HashSet();
0844: }
0845: modelsToSync.add(model);
0846: }
0847:
0848: public void removeFromModelsToSync(Model model) {
0849: if (modelsToSync != null) {
0850: modelsToSync.remove(model);
0851: }
0852: }
0853:
0854: /**
0855: * Synchronize all source and config models with their underlying buffers.
0856: */
0857: protected void syncAll() {
0858: ArrayList errorAccumulator = new ArrayList();
0859: if (modelsToSync == null) {
0860: modelsToSync = new HashSet();
0861: // Due to the fact that there is some resetting of errors and such happening on each sync,
0862: // we need to gather up the errors and present them at the end
0863: Model model = configModel;
0864: if (model.isValid()) {
0865: ParserAnnotation[] errors = model.getErrors();
0866: if (errors.length > 0) {
0867: for (int j = 0, max = errors.length; j < max; j++) {
0868: errorAccumulator.add(errors[j]);
0869: }
0870: }
0871: }
0872:
0873: Collection orderedModels = evalOrderModels(getModelsMap()
0874: .values());
0875: for (Iterator i = orderedModels.iterator(); i.hasNext();) {
0876: model = (Model) i.next();
0877: if (model.isValid()) {
0878: model.sync();
0879: ParserAnnotation[] errors = model.getErrors();
0880: if (errors.length > 0) {
0881: for (int j = 0, max = errors.length; j < max; j++) {
0882: errorAccumulator.add(errors[j]);
0883: }
0884: }
0885: }
0886: }
0887: } else {
0888: for (Iterator it = modelsToSync.iterator(); it.hasNext();) {
0889: Model model = (Model) it.next();
0890: it.remove();
0891: model.sync();
0892: ParserAnnotation[] errors = model.getErrors();
0893: if (errors.length > 0) {
0894: for (int j = 0, max = errors.length; j < max; j++) {
0895: errorAccumulator.add(errors[j]);
0896: }
0897: }
0898: }
0899: }
0900: if (errorAccumulator.size() > 0) {
0901: showSyncErrors(errorAccumulator, true);
0902: }
0903: }
0904:
0905: protected void releaseProjectClassLoader() {
0906: if (classLoader != null) {
0907: classLoader = null;
0908: }
0909: if (classPath != null) {
0910: classPath.removePropertyChangeListener(classPathListener);
0911: classPathListener = null;
0912: classPath = null;
0913: }
0914: }
0915:
0916: protected void showSyncErrors(ArrayList errors, boolean printPreface) {
0917: if (errors.size() == 0)
0918: return;
0919: if (printPreface) {
0920: InSyncServiceProvider.get().getRaveErrorHandler()
0921: .displayError(
0922: NbBundle.getMessage(ModelSet.class,
0923: "TXT_ErrorsOnOpenProject1")); // NOI18N
0924: InSyncServiceProvider.get().getRaveErrorHandler()
0925: .displayError(
0926: NbBundle.getMessage(ModelSet.class,
0927: "TXT_ErrorsOnOpenProject2")); // NOI18N
0928: }
0929: for (Iterator i = errors.iterator(); i.hasNext();) {
0930: StringBuffer sb = new StringBuffer(200);
0931: final ParserAnnotation err = (ParserAnnotation) i.next();
0932: // TODO We should find out why err.getFileObject() returns null, but Tor needs this to fix bug 6349268, even though I could
0933: // not reproduce to identify the source of the null :(
0934: if (err.getFileObject() == null) {
0935: sb.append("unknown");
0936: } else {
0937: sb.append(err.getFileObject().getNameExt());
0938: }
0939: sb.append(':');
0940: sb.append(err.getLine());
0941: sb.append(':');
0942: sb.append(err.getColumn());
0943: sb.append(':');
0944: sb.append(' ');
0945: sb.append(err.getMessage());
0946: // // XXX Todo - add output listener suitable for this location
0947: // OutputListener listener = new OutputListener() {
0948: // public void outputLineSelected(OutputEvent ev) {
0949: // }
0950: // public void outputLineAction(OutputEvent ev) {
0951: // // <markup_separation>
0952: //// Util.show(null, err.getFileObject(), err.getLine(),
0953: //// 0, true);
0954: // // ====
0955: // MarkupService.show(err.getFileObject(), err.getLine(), 0, true);
0956: // // </markup_separation>
0957: // }
0958: // public void outputLineCleared (OutputEvent ev) {
0959: // }
0960: // };
0961: // MarkupService.displayError(sb.toString(), listener);
0962: InSyncServiceProvider.get().getRaveErrorHandler()
0963: .displayErrorForFileObject(sb.toString(),
0964: err.getFileObject(), err.getLine(),
0965: err.getColumn());
0966: }
0967: InSyncServiceProvider.get().getRaveErrorHandler()
0968: .selectErrors();
0969: }
0970:
0971: /**
0972: * Flush all source and config models to their underlying buffers.
0973: */
0974: protected void flushAll() {
0975: for (Iterator i = getModelsMap().values().iterator(); i
0976: .hasNext();)
0977: ((Model) i.next()).flush();
0978: }
0979:
0980: /**
0981: * Save all source and config models to their underlying buffers.
0982: */
0983: protected void saveAll() {
0984: for (Iterator i = getModelsMap().values().iterator(); i
0985: .hasNext();)
0986: ((Model) i.next()).saveUnits();
0987: }
0988:
0989: class ClassPathListener implements PropertyChangeListener {
0990: public void propertyChange(PropertyChangeEvent event) {
0991: classPathChanged();
0992: }
0993: }
0994:
0995: class ModelCreateVisitor extends FileObjectVisitor {
0996: protected ArrayList modelsAdded = new ArrayList();
0997:
0998: protected void visitImpl(FileObject file) {
0999: if (!models.containsKey(file)) {
1000: for (Iterator i = getFactories().iterator(); i
1001: .hasNext();) {
1002: Model.Factory mf = (Model.Factory) i.next();
1003: Model m = mf.newInstance(ModelSet.this , file);
1004: if (m != null) {
1005: //FacesModel may be created because of a java file during
1006: //CVS update, therefore use primary file from the model
1007: //to add into models map
1008: addModel(m.getFile(), m);
1009: modelsAdded.add(m);
1010: break;
1011: }
1012: }
1013: }
1014: }
1015:
1016: public List getModelsAdded() {
1017: return modelsAdded;
1018: }
1019: }
1020:
1021: /**
1022: * Respond to changes in the project class path by updating the models.
1023: *
1024: * @see com.sun.rave.project.model.ProjectContentChangeListener#classPathChanged(com.sun.rave.project.model.ProjectContentChangeEvent)
1025: */
1026: public void classPathChanged() {
1027: releaseProjectClassLoader();
1028: getProjectClassLoader();
1029: }
1030:
1031: /**
1032: * Provide models in such a way as to cause higher scoped models to be ordered first.
1033: * This only works if we assume that lower scoped models references values from higher scoped models.
1034: * If higher scoped models references lower ones, then this will not help much.
1035: *
1036: * @param modelsToOrder
1037: * @return
1038: */
1039: protected Collection evalOrderModels(Collection modelsToOrder) {
1040: return modelsToOrder;
1041: }
1042:
1043: protected FileObject getLocalFileObject(FileObject fileObject) {
1044: if (fileObject == null) {
1045: return null;
1046: }
1047:
1048: // What does virtual actually mean, should I handle these as well ?
1049: if (fileObject.isVirtual())
1050: return null;
1051:
1052: File file = FileUtil.toFile(fileObject);
1053: if (file == null) {
1054: return null;
1055: }
1056: //Check if the file is non sharable
1057: if (SharabilityQuery.getSharability(file) == SharabilityQuery.NOT_SHARABLE) {
1058: return null;
1059: }
1060: if (hasActiveLockFileSigns(fileObject)) {
1061: return null;
1062: }
1063: FileObject projectRoot = getProject().getProjectDirectory();
1064: if (projectRoot == null
1065: || !FileUtil.isParentOf(projectRoot, fileObject)) {
1066: return null;
1067: }
1068: return fileObject;
1069: }
1070:
1071: public void fileAttributeChanged(FileAttributeEvent fe) {
1072: }
1073:
1074: public void fileFolderCreated(FileEvent fe) {
1075: // Dont really care about folders do we ?
1076: }
1077:
1078: public void fileChanged(FileEvent fe) {
1079: // Do we need to listen to these, dont think so ?
1080: }
1081:
1082: public void fileDataCreated(FileEvent fe) {
1083: FileObject fileObject = getLocalFileObject(fe.getFile());
1084: if (fileObject == null) {
1085: return;
1086: }
1087:
1088: if (fileObject.getAttribute("NBIssue81746Workaround") == Boolean.TRUE) { // NOI18N
1089: try {
1090: fileObject.setAttribute("NBIssue81746Workaround", null); // NOI18N
1091: } catch (java.io.IOException ex) {
1092: ex.printStackTrace();
1093: }
1094: // XXX NB issue #81746
1095: // This will be handled in the createFromTemplate listener, see the issue.
1096: return;
1097: }
1098:
1099: processFileDataCreated(fileObject);
1100: }
1101:
1102: private FileObject getOurFileObject(FileObject fileObject) {
1103: fileObject = getLocalFileObject(fileObject);
1104: if (fileObject == null)
1105: return null;
1106: // we should create Model only if the file is under document root or source root
1107: if (!FileUtil.isParentOf(JsfProjectUtils
1108: .getDocumentRoot(getProject()), fileObject)
1109: && !FileUtil.isParentOf(JsfProjectUtils
1110: .getSourceRoot(getProject()), fileObject)) {
1111: return null;
1112: }
1113: return fileObject;
1114: }
1115:
1116: // XXX NB issue #
1117: public void processFileDataCreated(final FileObject fileObject) {
1118: // we should create Model only if the file is under document root or source root
1119: if (!FileUtil.isParentOf(JsfProjectUtils
1120: .getDocumentRoot(getProject()), fileObject)
1121: && !FileUtil.isParentOf(JsfProjectUtils
1122: .getSourceRoot(getProject()), fileObject)) {
1123: return;
1124: }
1125:
1126: // Do this outside of refactoring session, as we cannot guarantee when the Java or the JSP file will be
1127: // "added", this way we wait until everything is done and we have all the files already moved prior
1128: // to building the models
1129: /*//NB6.0
1130: MdrInSyncSynchronizer.get().doOutsideOfRefactoringSession(new Runnable() {
1131: public void run() {
1132: */
1133: ModelCreateVisitor visitor = new ModelCreateVisitor();
1134: if (fileObject.isFolder()) {
1135: visitor.traverse(fileObject);
1136: } else {
1137: visitor.visit(fileObject);
1138: }
1139: Collection modelsAdded = visitor.getModelsAdded();
1140: for (Iterator i = modelsAdded.iterator(); i.hasNext();) {
1141: Model model = (Model) i.next();
1142: // We do a sync here to make sure that the model REALLY is a valid one
1143: // The visitor above can create models that should not really be models
1144: // but we can only find out once we perform a sync. If as a result
1145: // of the sync, the model has no owner, this indicates that sync destroy'ed
1146: // the model and that it should not be a model after all
1147: FileObject file = model.getFile();
1148: try {
1149: //Set an attribute to indicate the file is newly created which is
1150: //used to decide the addition of cross referencing accessors
1151: file.setAttribute("NewFile", Boolean.TRUE); //NOI18N
1152: model.sync();
1153: } catch (IOException ioe) {
1154: assert Trace.trace("insync.model",
1155: "Failed to set the attribute: "
1156: + model.getFile()); //NOI18N
1157: } finally {
1158: try {
1159: file.setAttribute("NewFile", null); //NOI18N
1160: } catch (IOException ioe) {
1161: assert Trace.trace("insync.model",
1162: "Failed to reset the attribute: "
1163: + model.getFile()); //NOI18N
1164: }
1165: }
1166: if (model.isValid()) {
1167: model.saveUnits();
1168: } else {
1169: models.remove(file);
1170: model = null;
1171: }
1172: }
1173: /*
1174: }
1175: });
1176: //*/
1177: }
1178:
1179: public void fileDeleted(final FileEvent event) {
1180: }
1181:
1182: public void fileRenamed(final FileRenameEvent event) {
1183: final FileObject fileObject = event.getFile();
1184: final String oldName = event.getName();
1185: final String newName = fileObject.getName();
1186: final String extension = fileObject.getExt();
1187: //If the file is renamed to non sharable file(for example during cvs conflicts)
1188: //it is necessary to remove the model
1189: final boolean needToRemove = getLocalFileObject(fileObject) == null ? true
1190: : false;
1191: final Model[] models = getModels();
1192:
1193: for (int i = 0; i < models.length; i++) {
1194: Model model = models[i];
1195: model.fileRenamed(oldName, newName, extension, fileObject,
1196: needToRemove);
1197: }
1198: }
1199:
1200: private/*static*/class ModelSetOperationListener implements
1201: OperationListener {
1202: public void operationPostCreate(OperationEvent ev) {
1203: }
1204:
1205: public void operationCopy(OperationEvent.Copy ev) {
1206: }
1207:
1208: public void operationMove(OperationEvent.Move ev) {
1209: }
1210:
1211: public void operationDelete(OperationEvent ev) {
1212: final FileObject fileObject = getLocalFileObject(ev
1213: .getObject().getPrimaryFile());
1214: if (fileObject == null) {
1215: return;
1216: }
1217:
1218: final Model model = getModel(fileObject);
1219: if (model != null) {
1220: SwingUtilities.invokeLater(new Runnable() {
1221: public void run() {
1222: ModelSet modelSet = model.getOwner();
1223: if (modelSet != null) {
1224: modelSet.removeModel(model);
1225: }
1226: }
1227: });
1228: }
1229: }
1230:
1231: public void operationRename(OperationEvent.Rename ev) {
1232: }
1233:
1234: public void operationCreateShadow(OperationEvent.Copy ev) {
1235: }
1236:
1237: public void operationCreateFromTemplate(OperationEvent.Copy ev) {
1238: FileObject fileObject = getOurFileObject(ev.getObject()
1239: .getPrimaryFile());
1240: if (fileObject == null) {
1241: return;
1242: }
1243:
1244: // XXX NB issue #81746.
1245: processFileDataCreated(fileObject);
1246: }
1247: } // End of ModelSetOperationListener.
1248:
1249: public void removeModel(Model model) {
1250: FileObject fileObject = model.getFile();
1251: models.remove(fileObject);
1252: fireModelRemoved(model);
1253: model.destroy();
1254: }
1255:
1256: }
|