Java tutorial
/******************************************************************************* * Copyright (c) 2003, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jst.j2ee.internal.common.classpath; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jdt.core.IAccessRule; import org.eclipse.jdt.core.IClasspathAttribute; import org.eclipse.jdt.core.IClasspathContainer; import org.eclipse.jdt.core.IClasspathEntry; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jst.common.jdt.internal.classpath.ClasspathDecorations; import org.eclipse.jst.common.jdt.internal.classpath.ClasspathDecorationsManager; import org.eclipse.jst.common.jdt.internal.javalite.IJavaProjectLite; import org.eclipse.jst.common.jdt.internal.javalite.JavaCoreLite; import org.eclipse.jst.j2ee.componentcore.J2EEModuleVirtualComponent; import org.eclipse.jst.j2ee.internal.common.ClasspathLibraryExpander; import org.eclipse.jst.j2ee.internal.common.J2EECommonMessages; import org.eclipse.jst.j2ee.internal.plugin.J2EEPlugin; import org.eclipse.jst.j2ee.project.EarUtilities; import org.eclipse.jst.j2ee.project.JavaEEProjectUtilities; import org.eclipse.wst.common.componentcore.ComponentCore; import org.eclipse.wst.common.componentcore.internal.StructureEdit; import org.eclipse.wst.common.componentcore.internal.builder.IDependencyGraph; import org.eclipse.wst.common.componentcore.internal.flat.IFlatFolder; import org.eclipse.wst.common.componentcore.internal.flat.IFlatResource; import org.eclipse.wst.common.componentcore.internal.resources.VirtualArchiveComponent; import org.eclipse.wst.common.componentcore.internal.resources.VirtualReference; import org.eclipse.wst.common.componentcore.resources.IVirtualComponent; import org.eclipse.wst.common.componentcore.resources.IVirtualReference; /** * This classpath container is based on the Component references; not the manifest entries. Other * mechanisms are in place to ensure that the component references are updated when the manifest is * updated, and also to make sure the manifest is updated when the component references are updated. * */ public class J2EEComponentClasspathContainer implements IClasspathContainer { public static final String CONTAINER_ID = "org.eclipse.jst.j2ee.internal.module.container"; //$NON-NLS-1$ public static final IPath CONTAINER_PATH = new Path(CONTAINER_ID); private static IPath WEBLIB = new Path("/WEB-INF/lib"); //$NON-NLS-1$ private static ClasspathDecorationsManager decorationsManager = new ClasspathDecorationsManager( J2EEPlugin.PLUGIN_ID); public static ClasspathDecorationsManager getDecorationsManager() { return decorationsManager; } private static Map<String, Object> onlyManifestRefs = new HashMap<String, Object>(); static { onlyManifestRefs.put(IVirtualComponent.REQUESTED_REFERENCE_TYPE, J2EEModuleVirtualComponent.ONLY_MANIFEST_REFERENCES); } private IPath containerPath; private IJavaProject javaProject; private IJavaProjectLite javaProjectLite; private IClasspathEntry[] entries = new IClasspathEntry[0]; private static Map<Integer, Integer> keys = new Hashtable<Integer, Integer>(); private static int MAX_RETRIES = 10; private static Map<Integer, Integer> retries = new Hashtable<Integer, Integer>(); static class LastUpdate implements Serializable { private static final long serialVersionUID = 362498820763181265L; private boolean exportEntries = true; //the default behavior is to always export these dependencies private int baseRefCount = 0; // count of references returned directly from a component private int baseLibRefCount = 0; // count of references resolved by EAR 5 lib directory private int refCount = 0; private boolean[] isBinary = new boolean[refCount]; transient private IPath[] paths = new IPath[refCount]; transient boolean needToVerify = true; //only used for serialization private String[] pathStrings = null; @Override public boolean equals(Object o) { if (this == o) { return true; } else if (o == null) { return false; } else if (o instanceof LastUpdate) { LastUpdate other = (LastUpdate) o; if (this.exportEntries != other.exportEntries) { return false; } else if (this.baseRefCount != other.baseRefCount) { return false; } else if (this.baseLibRefCount != other.baseLibRefCount) { return false; } else if (this.refCount != other.refCount) { return false; } else if (this.isBinary.length != other.isBinary.length) { return false; } else if (this.paths.length != other.paths.length) { return false; } for (int i = 0; i < isBinary.length; i++) { if (this.isBinary[i] != other.isBinary[i]) { return false; } } for (int i = 0; i < paths.length; i++) { if (this.paths[i] == null && other.paths[i] != null) { return false; } else if (!this.paths[i].equals(other.paths[i])) { return false; } } return true; } else { return false; } } @Override public int hashCode() { return 3 * baseRefCount + 5 * baseLibRefCount + 7 * refCount + 11 * isBinary.length; } private void writeObject(java.io.ObjectOutputStream out) throws IOException { pathStrings = new String[refCount]; for (int i = 0; i < paths.length; i++) { if (paths[i] != null) { pathStrings[i] = paths[i].toString(); } } out.defaultWriteObject(); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); needToVerify = true; paths = new IPath[refCount]; for (int i = 0; i < pathStrings.length; i++) { if (pathStrings[i] != null) { paths[i] = new Path(pathStrings[i]); } } } private boolean areSame(IVirtualComponent comp, int i) { if (comp.isBinary() != isBinary[i]) { return false; } IPath path = null; if (comp.isBinary()) path = (IPath) comp.getAdapter(IPath.class); else path = comp.getProject().getFullPath(); if (!path.equals(paths[i])) { return false; } return true; } } private LastUpdate lastUpdate = new LastUpdate(); public J2EEComponentClasspathContainer(IPath path, IJavaProject javaProject) { this.containerPath = path; this.javaProject = javaProject; this.javaProjectLite = JavaCoreLite.create(javaProject); } private boolean requiresUpdate() { IVirtualComponent component = ComponentCore.createComponent(javaProjectLite.getProject()); if (component == null) { return false; } IVirtualReference[] refs = component.getReferences(onlyManifestRefs); // avoid updating the container if references haven't changed if (refs.length == lastUpdate.baseRefCount) { for (int i = 0; i < lastUpdate.baseRefCount; i++) { IVirtualComponent comp = null; comp = refs[i].getReferencedComponent(); if (!lastUpdate.areSame(comp, i)) { return true; } } List<IVirtualReference> earRefs = getBaseEARLibRefs(component); if (earRefs.size() != lastUpdate.baseLibRefCount) { return true; } List<IVirtualReference> refsList = new ArrayList<IVirtualReference>(); Set<IVirtualComponent> refedComps = new HashSet<IVirtualComponent>(); refedComps.add(component); for (int i = 0; i < refs.length; i++) { refsList.add(refs[i]); refedComps.add(refs[i].getReferencedComponent()); } int i = lastUpdate.baseRefCount; for (IVirtualReference earRef : earRefs) { IVirtualComponent comp = earRef.getReferencedComponent(); // check if the referenced component is already visited - avoid cycles in the build path if (!refedComps.contains(comp)) { if (i == lastUpdate.refCount) { return true; // found something new and need update } // visit the referenced component refsList.add(earRef); refedComps.add(comp); if (!lastUpdate.areSame(comp, i)) { return true; } i++; } } if (i != lastUpdate.refCount) { return true; // didn't find them all } return false; } return true; } private void update(LastUpdate restoreState) { if (restoreState != null) { // performance; restore state from last session lastUpdate = restoreState; List<IClasspathEntry> entriesList = new ArrayList<IClasspathEntry>(); for (int i = 0; i < lastUpdate.paths.length; i++) { if (lastUpdate.paths[i] != null) { IClasspathEntry newEntry = createEntry(restoreState, i); entriesList.add(newEntry); } } entries = new IClasspathEntry[entriesList.size()]; for (int i = 0; i < entries.length; i++) { entries[i] = entriesList.get(i); } return; } IVirtualComponent component = ComponentCore.createComponent(javaProjectLite.getProject()); if (component == null) { return; } Integer key = null; if (!javaProjectLite.getProject().getFile(StructureEdit.MODULE_META_FILE_NAME).exists()) { Integer hashCode = new Integer(javaProjectLite.getProject().hashCode()); key = keys.get(hashCode); if (key == null) { keys.put(hashCode, hashCode); key = hashCode; } Integer retryCount = retries.get(key); if (retryCount == null) { retryCount = new Integer(1); } else if (retryCount.intValue() > MAX_RETRIES) { return; } else { retryCount = new Integer(retryCount.intValue() + 1); } retries.put(key, retryCount); J2EEComponentClasspathUpdater.getInstance().queueUpdate(javaProjectLite.getProject()); return; } IVirtualReference[] refs = component.getReferences(onlyManifestRefs); List<IVirtualReference> refsList = new ArrayList<IVirtualReference>(); Set<IVirtualComponent> refedComps = new HashSet<IVirtualComponent>(); refedComps.add(component); for (IVirtualReference ref : refs) { if (ref.getDependencyType() == IVirtualReference.DEPENDENCY_TYPE_USES) { refsList.add(ref); refedComps.add(ref.getReferencedComponent()); } } lastUpdate.baseRefCount = refsList.size(); List<IVirtualReference> earLibReferences = getBaseEARLibRefs(component); lastUpdate.baseLibRefCount = earLibReferences.size(); for (IVirtualReference earRef : earLibReferences) { IVirtualComponent earRefComp = earRef.getReferencedComponent(); // check if the referenced component is already visited - avoid cycles in the build path if (!refedComps.contains(earRefComp)) { // visit the referenced component refsList.add(earRef); refedComps.add(earRefComp); } } //iterate with i index because this list may be augmented during iteration for (int i = 0; i < refsList.size(); i++) { IVirtualComponent comp = refsList.get(i).getReferencedComponent(); if (comp.isBinary()) { IVirtualReference[] binaryRefs = comp.getReferences(); for (int j = 0; j < binaryRefs.length; j++) { if (!refedComps.contains(binaryRefs[j].getReferencedComponent())) { refsList.add(binaryRefs[j]); refedComps.add(binaryRefs[j].getReferencedComponent()); } } } } lastUpdate.refCount = refsList.size(); lastUpdate.isBinary = new boolean[lastUpdate.refCount]; lastUpdate.paths = new IPath[lastUpdate.refCount]; boolean isWeb = JavaEEProjectUtilities.isDynamicWebProject(component.getProject()); boolean shouldAdd = true; List<IClasspathEntry> entriesList = new ArrayList<IClasspathEntry>(); try { boolean useJDTToControlExport = J2EEComponentClasspathContainerUtils .getDefaultUseEARLibrariesJDTExport(); if (useJDTToControlExport) { //if the default is not enabled, then check whether the container is being exported IClasspathEntry[] rawEntries = javaProjectLite.readRawClasspath(); for (int i = 0; i < rawEntries.length; i++) { IClasspathEntry entry = rawEntries[i]; if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { if (entry.getPath().equals(CONTAINER_PATH)) { lastUpdate.exportEntries = entry.isExported(); break; } } } } IVirtualReference ref = null; IVirtualComponent comp = null; for (int i = 0; i < refsList.size(); i++) { ref = refsList.get(i); comp = ref.getReferencedComponent(); lastUpdate.isBinary[i] = comp.isBinary(); shouldAdd = !(isWeb && ref.getRuntimePath().equals(WEBLIB)); if (!shouldAdd) { continue; } if (lastUpdate.isBinary[i]) { if (comp instanceof VirtualArchiveComponent) { VirtualArchiveComponent archiveComp = (VirtualArchiveComponent) comp; if (archiveComp.getArchiveType().equals(VirtualArchiveComponent.CLASSPATHARCHIVETYPE)) { // do not process components dynamically computed from the Java classpath continue; } } lastUpdate.paths[i] = (IPath) comp.getAdapter(IPath.class); IClasspathEntry newEntry = createEntry(lastUpdate, i); entriesList.add(newEntry); } else { IProject project = comp.getProject(); lastUpdate.paths[i] = project.getFullPath(); IClasspathEntry newEntry = createEntry(lastUpdate, i); entriesList.add(newEntry); } } } finally { entries = new IClasspathEntry[entriesList.size()]; for (int i = 0; i < entries.length; i++) { entries[i] = entriesList.get(i); } J2EEComponentClasspathContainerStore.saveState(javaProjectLite.getProject().getName(), lastUpdate); } } private IClasspathEntry createEntry(LastUpdate lastUpdate, int index) { if (lastUpdate.isBinary[index]) { ClasspathDecorations dec = decorationsManager.getDecorations(getPath().toString(), lastUpdate.paths[index].toString()); IPath srcpath = null; IPath srcrootpath = null; IClasspathAttribute[] attrs = {}; IAccessRule[] access = {}; if (dec != null) { srcpath = dec.getSourceAttachmentPath(); srcrootpath = dec.getSourceAttachmentRootPath(); attrs = dec.getExtraAttributes(); } IClasspathEntry newEntry = JavaCoreLite.newLibraryEntry(lastUpdate.paths[index], srcpath, srcrootpath, access, attrs, lastUpdate.exportEntries); return newEntry; } IClasspathEntry newEntry = JavaCoreLite.newProjectEntry(lastUpdate.paths[index], lastUpdate.exportEntries); return newEntry; } private List<IVirtualReference> getBaseEARLibRefs(IVirtualComponent component) { List<IVirtualReference> libRefs = new ArrayList<IVirtualReference>(); // check for the references in the lib dirs of the referencing EARs IVirtualComponent[] referencingList = component.getReferencingComponents(); for (IVirtualComponent referencingComp : referencingList) { // check if the referencing component is an EAR if (EarUtilities.isEARProject(referencingComp.getProject())) { IVirtualComponent earComp = referencingComp; // retrieve the EAR's library directory String libDir = EarUtilities.getEARLibDir(earComp); // if the EAR version is lower than 5, then the library directory will be null // or if it is the empty string, do nothing. if (libDir != null && libDir.trim().length() != 0) { IPath libDirPath = new Path(libDir).makeRelative(); // check if the component itself is not in the library directory of this EAR - avoid cycles in the build path IVirtualReference ref = earComp.getReference(component.getName()); if (ref != null) { IPath refPath = ref.getRuntimePath(); String archiveName = ref.getArchiveName(); if (archiveName != null) { // this check is needed to handle the scenario where the ref.getRuntimePath() is "/" // and the archive name is "/lib/foo.jar" refPath = refPath.append(archiveName); if (refPath.segmentCount() > 0) { refPath = refPath.removeLastSegments(1); } } refPath = refPath.makeRelative(); boolean onlyBinary = false; // If this component is in the library directory, we will allow only binary entries to be // added, to avoid cycles in the build path if (libDirPath.equals(refPath)) { onlyBinary = true; } // retrieve the referenced components from the EAR IVirtualReference[] earRefs = earComp.getReferences(); for (IVirtualReference earRef : earRefs) { if (earRef.getDependencyType() == IVirtualReference.DEPENDENCY_TYPE_USES) { // check if the referenced component is in the library directory IPath runtimePath = earRef.getRuntimePath().makeRelative(); boolean isInLibDir = libDirPath.equals(runtimePath); if (!isInLibDir && earRef.getArchiveName() != null) { IPath fullPath = earRef.getRuntimePath().append(earRef.getArchiveName()); isInLibDir = fullPath.removeLastSegments(1).makeRelative().equals(libDirPath); } if (isInLibDir) { if (!onlyBinary || (onlyBinary && earRef.getReferencedComponent().isBinary())) libRefs.add(earRef); } } } //add EAR classpath container refs try { ClasspathLibraryExpander classpathLibExpander = new ClasspathLibraryExpander(earComp); IFlatResource flatLibResource = classpathLibExpander.fetchResource(libDirPath); if (flatLibResource instanceof IFlatFolder) { IFlatFolder flatLibFolder = (IFlatFolder) flatLibResource; IFlatResource[] flatLibs = flatLibFolder.members(); for (IFlatResource flatResource : flatLibs) { File file = (File) flatResource.getAdapter(File.class); if (file != null) { IVirtualComponent dynamicComponent = new VirtualArchiveComponent( earComp.getProject(), VirtualArchiveComponent.LIBARCHIVETYPE + "/" //$NON-NLS-1$ + file.getAbsolutePath(), new Path(libDir)); IVirtualReference dynamicRef = ComponentCore.createReference(earComp, dynamicComponent); ((VirtualReference) dynamicRef).setDerived(true); dynamicRef.setArchiveName(file.getName()); libRefs.add(dynamicRef); } } } } catch (CoreException e) { J2EEPlugin.logError(e); } } } } } return libRefs; } public static J2EEComponentClasspathContainer install(IPath containerPath, IJavaProject javaProject, LastUpdate restoreState) { try { J2EEComponentClasspathUpdater.getInstance().pauseUpdates(); final IJavaProject[] projects = new IJavaProject[] { javaProject }; final J2EEComponentClasspathContainer container = new J2EEComponentClasspathContainer(containerPath, javaProject); container.update(restoreState); final IClasspathContainer[] conts = new IClasspathContainer[] { container }; try { JavaCore.setClasspathContainer(containerPath, projects, conts, null); } catch (JavaModelException e) { J2EEPlugin.logError(e); } return container; } finally { J2EEComponentClasspathUpdater.getInstance().resumeUpdates(); } } public static void install(final IPath containerPath, final IJavaProject javaProject) { final String projectName = javaProject.getProject().getName(); LastUpdate restoreState = J2EEComponentClasspathContainerStore.getRestoreState(projectName); boolean needToVerify = false; if (null != restoreState) { synchronized (restoreState) { needToVerify = restoreState.needToVerify; restoreState.needToVerify = false; } } final J2EEComponentClasspathContainer container = install(containerPath, javaProject, restoreState); if (needToVerify) { Job verifyJob = new Job(Messages.J2EEComponentClasspathUpdater_Verify_EAR_Libraries) { @Override protected IStatus run(IProgressMonitor monitor) { container.refresh(); return Status.OK_STATUS; } }; verifyJob.setSystem(true); verifyJob.setRule(ResourcesPlugin.getWorkspace().getRoot()); verifyJob.schedule(); } } public void refresh(boolean force) { if (!force) { if (IDependencyGraph.INSTANCE.isStale()) { //avoid deadlock https://bugs.eclipse.org/bugs/show_bug.cgi?id=334050 //if the data is stale abort and attempt to update again in the near future J2EEComponentClasspathUpdater.getInstance().queueUpdate(javaProject.getProject()); return; } } if (force || requiresUpdate()) { install(containerPath, javaProject, null); } } public void refresh() { refresh(false); } public IClasspathEntry[] getClasspathEntries() { return entries; } public String getDescription() { return J2EECommonMessages.J2EE_MODULE_CLASSPATH_CONTAINER_NAME; } public int getKind() { return K_APPLICATION; } public IPath getPath() { return containerPath; } }