blob: 08db6172cd3b464014a6c4a80451a83ca8bbcb35 [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.j2ee.componentcore.J2EEModuleVirtualComponent;
import org.eclipse.jst.j2ee.componentcore.util.EARVirtualComponent;
import org.eclipse.jst.j2ee.internal.J2EEConstants;
import org.eclipse.jst.j2ee.internal.common.J2EECommonMessages;
import org.eclipse.jst.j2ee.internal.plugin.J2EEPlugin;
import org.eclipse.jst.j2ee.model.IModelProvider;
import org.eclipse.jst.j2ee.model.ModelProviderManager;
import org.eclipse.jst.j2ee.project.EarUtilities;
import org.eclipse.jst.j2ee.project.JavaEEProjectUtilities;
import org.eclipse.jst.javaee.application.Application;
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 IClasspathEntry[] entries = new IClasspathEntry[0];
private boolean exportEntries = true; //the default behavior is to always export these dependencies
private static Map keys = new Hashtable();
private static int MAX_RETRIES = 10;
private static Map retries = new Hashtable();
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;
} else {
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;
}
private boolean requiresUpdate() {
IVirtualComponent component = ComponentCore.createComponent(javaProject.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;
} else {
List refsList = new ArrayList();
Set refedComps = new HashSet();
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(javaProject.getProject());
if (component == null) {
return;
}
Object key = null;
if(!javaProject.getProject().getFile(StructureEdit.MODULE_META_FILE_NAME).exists()){
Integer hashCode = new Integer(javaProject.getProject().hashCode());
key = keys.get(hashCode);
if(key == null){
keys.put(hashCode, hashCode);
key = hashCode;
}
Integer retryCount = (Integer)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(javaProject.getProject());
return;
} else {
if(key != null){
retries.remove(key);
keys.remove(key);
}
}
IVirtualComponent comp = null;
IVirtualReference ref = null;
IVirtualReference[] refs = component instanceof J2EEModuleVirtualComponent ? ((J2EEModuleVirtualComponent)component).getReferences(false, true): component.getReferences();
lastUpdate.baseRefCount = refs.length;
List refsList = new ArrayList();
Set refedComps = new HashSet();
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 = ((IVirtualReference)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 entriesList = new ArrayList();
try {
IJavaProject javaProject = JavaCore.create(component.getProject());
boolean useJDTToControlExport = J2EEComponentClasspathContainerUtils.getDefaultUseEARLibrariesJDTExport();
if(useJDTToControlExport){
//if the default is not enabled, then check whether the container is being exported
IClasspathEntry [] rawEntries = javaProject.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 = (IVirtualReference)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 = JavaCore.newLibraryEntry( lastUpdate.paths[i], srcpath, srcrootpath, access, attrs, exportEntries );
entriesList.add(newEntry);
} else {
IProject project = comp.getProject();
lastUpdate.paths[i] = project.getFullPath();
entriesList.add(JavaCore.newProjectEntry(lastUpdate.paths[i], exportEntries));
}
}
} finally {
entries = new IClasspathEntry[entriesList.size()];
for (int i = 0; i < entries.length; i++) {
entries[i] = (IClasspathEntry) 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 = 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);
}
private boolean isUpdating = false;
public IClasspathEntry[] getClasspathEntries() {
if(!isUpdating){
if(this != J2EEComponentClasspathContainerUtils.getInstalledEARLibrariesContainer(javaProject.getProject())){
try {
isUpdating = true;
update();
} finally{
isUpdating = false;
}
}
}
return entries;
}
public String getDescription() {
return J2EECommonMessages.J2EE_MODULE_CLASSPATH_CONTAINER_NAME;
}
public int getKind() {
return K_APPLICATION;
}
public IPath getPath() {
return containerPath;
}
/**
* Get the library directory from an EAR virtual component
*
* @param earComponent
* the EAR virtual component
*
* @return a runtime representation of the library directory path or null if
* the EAR's version is lower than 5
*/
private String getEARLibDir(EARVirtualComponent earComponent) {
// check if the EAR component's version is 5 or greater
IProject earProject = earComponent.getProject();
if (!JavaEEProjectUtilities.isJEEComponent(earComponent, JavaEEProjectUtilities.DD_VERSION)) {
return null;
}
// default lib dir if there is no deployment descriptor
// or if the deployment descriptor does not override
String libDir = J2EEConstants.EAR_DEFAULT_LIB_DIR;
// retrieve the model provider
IModelProvider modelProvider = ModelProviderManager.getModelProvider(earProject);
if (modelProvider == null){
return libDir;
}
// retrieve the EAR's deployment descriptor model object
Application app = (Application) modelProvider.getModelObject(new Path(J2EEConstants.APPLICATION_DD_URI));
if (app == null){
return libDir;
}
// retrieve the library directory from the model
String ddLibDir = app.getLibraryDirectory();
if (ddLibDir != null) {
libDir = ddLibDir;
}
return libDir;
}
}