blob: d126f28ceb02429d6db7f4d1edcafebd9ac7f38c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2006 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.util.ArrayList;
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.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
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.componentcore.util.EARVirtualComponent;
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.resources.VirtualArchiveComponent;
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 IPath containerPath;
private IJavaProject javaProject;
private IJavaProjectLite javaProjectLite;
private IClasspathEntry[] entries = new IClasspathEntry[0];
private boolean exportEntries = true; //the default behavior is to always export these dependencies
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>();
private class LastUpdate {
private int baseRefCount = 0; // count of references returned directly from a component
private int refCount = 0;
private boolean[] isBinary = new boolean[refCount];
private IPath[] paths = new IPath[refCount];
private int baseLibRefCount = 0; // count of references resolved by EAR 5 lib directory
private boolean areSame(IVirtualComponent comp, int i){
if (comp.isBinary() != isBinary[i]) {
return false;
}
IPath path = null;
if (comp.isBinary()) {
VirtualArchiveComponent archiveComp = (VirtualArchiveComponent) comp;
java.io.File diskFile = archiveComp.getUnderlyingDiskFile();
if (diskFile.exists())
path = new Path(diskFile.getAbsolutePath());
else {
IFile iFile = archiveComp.getUnderlyingWorkbenchFile();
path = iFile.getFullPath();
}
} 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 instanceof J2EEModuleVirtualComponent ? ((J2EEModuleVirtualComponent)component).getReferences(false, true): component.getReferences();
// 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() {
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;
}
IVirtualComponent comp = null;
IVirtualReference ref = null;
IVirtualReference[] refs = component instanceof J2EEModuleVirtualComponent ? ((J2EEModuleVirtualComponent)component).getReferences(false, true): component.getReferences();
lastUpdate.baseRefCount = refs.length;
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());
}
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);
}
}
for(int i=0; i< refsList.size(); i++){
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)){
exportEntries = entry.isExported();
break;
}
}
}
}
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 (comp.isBinary()) {
VirtualArchiveComponent archiveComp = (VirtualArchiveComponent) comp;
if (archiveComp.getArchiveType().equals(VirtualArchiveComponent.CLASSPATHARCHIVETYPE)) {
// do not process components dynamically computed from the Java classpath
continue;
}
java.io.File diskFile = archiveComp.getUnderlyingDiskFile();
if (diskFile.exists()) {
lastUpdate.paths[i] = new Path(diskFile.getAbsolutePath());
} else {
IFile iFile = archiveComp.getUnderlyingWorkbenchFile();
lastUpdate.paths[i] = iFile.getFullPath();
}
ClasspathDecorations dec = decorationsManager.getDecorations( getPath().toString(), lastUpdate.paths[i].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[i], srcpath, srcrootpath, access, attrs, exportEntries );
entriesList.add(newEntry);
} else {
IProject project = comp.getProject();
lastUpdate.paths[i] = project.getFullPath();
entriesList.add(JavaCoreLite.newProjectEntry(lastUpdate.paths[i], exportEntries));
}
}
} finally {
entries = new IClasspathEntry[entriesList.size()];
for (int i = 0; i < entries.length; i++) {
entries[i] = entriesList.get(i);
}
}
}
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())) {
EARVirtualComponent earComp = (EARVirtualComponent) 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
if (libDir != null) {
// check if the component itself is not in the library directory of this EAR - avoid cycles in the build patch
if (!libDir.equals(earComp.getReference(component.getName()).getRuntimePath().toString())) {
// retrieve the referenced components from the EAR
IVirtualReference[] earRefs = earComp.getReferences();
for (IVirtualReference earRef : earRefs) {
// check if the referenced component is in the library directory
boolean isInLibDir = libDir.equals(earRef.getRuntimePath().toString());
if(!isInLibDir){
IPath fullPath = earRef.getRuntimePath().append(earRef.getArchiveName());
isInLibDir = fullPath.removeLastSegments(1).toString().equals(libDir);
}
if (isInLibDir) {
libRefs.add(earRef);
}
}
}
}
}
}
return libRefs;
}
public static void install(IPath containerPath, IJavaProject javaProject) {
try{
J2EEComponentClasspathUpdater.getInstance().pauseUpdates();
final IJavaProject[] projects = new IJavaProject[]{javaProject};
final J2EEComponentClasspathContainer container = new J2EEComponentClasspathContainer(containerPath, javaProject);
container.update();
final IClasspathContainer[] conts = new IClasspathContainer[]{container};
try {
JavaCore.setClasspathContainer(containerPath, projects, conts, null);
} catch (JavaModelException e) {
J2EEPlugin.logError(e);
}
} finally {
J2EEComponentClasspathUpdater.getInstance().resumeUpdates();
}
}
public void refresh(boolean force){
if(force || requiresUpdate()){
install(containerPath, javaProject);
}
}
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;
}
}