org.eclipse.jst.server.core.RuntimeClasspathProviderDelegate.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jst.server.core.RuntimeClasspathProviderDelegate.java

Source

/*******************************************************************************
 * Copyright (c) 2003, 2011 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.server.core;

import java.io.File;
import java.util.*;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
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.jst.server.core.internal.IMemento;
import org.eclipse.jst.server.core.internal.JavaServerPlugin;
import org.eclipse.jst.server.core.internal.RuntimeClasspathContainer;
import org.eclipse.jst.server.core.internal.Trace;
import org.eclipse.jst.server.core.internal.XMLMemento;
import org.eclipse.wst.server.core.IRuntime;

/**
 * A runtime classpath provider provides the classpath for a Java server runtime.
 * This provider is scoped by runtime type and may provide the classpath for multiple
 * runtime instances.
 * <p>
 * This abstract class is intended to be extended only by clients
 * to extend the <code>runtimeClasspathProviders</code> extension point.
 * </p>
 * <p>
 * <b>Provisional API:</b> This class/interface is part of an interim API that is still under development and expected to 
 * change significantly before reaching stability. It is being made available at this early stage to solicit feedback 
 * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken 
 * (repeatedly) as the API evolves.
 * </p>
 * 
 * @plannedfor 3.0
 */
public abstract class RuntimeClasspathProviderDelegate {
    protected class SourceAttachmentUpdate {
        String runtimeId;
        IPath entry;
        IPath sourceAttachmentPath;
        IPath sourceAttachmentRootPath;
        IClasspathAttribute[] attributes;
    }

    private volatile List<SourceAttachmentUpdate> sourceAttachments;

    private String extensionId;

    private Map<String, IPath> runtimePathMap = Collections.synchronizedMap(new HashMap<String, IPath>());

    private Map<String, Integer> previousClasspath = Collections.synchronizedMap(new HashMap<String, Integer>());

    public RuntimeClasspathProviderDelegate() {
        // default constructor
    }

    /**
     * Initializes this classpath provider with its life-long id.
     * <p>
     * This method is called by the framework.
     * Clients should never call this method.
     * </p>
     * @param id the extension id
     */
    public final void initialize(String id) {
        extensionId = id;
    }

    /**
     * Resolves (creates the classpath entries for) the classpath container with
     * the given runtime and the given classpath container id (returned from
     * getClasspathEntryIds()). If the classpath container cannot be resolved
     * (for instance, if the runtime does not exist), return null.
     * 
     * @param runtime the runtime to resolve the container for
     * @return an array of classpath entries for the container, or null if the
     *   container could not be resolved
     * @deprecated use resolveClasspathContainer(IProject, IRuntime) instead
     */
    public IClasspathEntry[] resolveClasspathContainer(IRuntime runtime) {
        return null;
    }

    /**
     * Resolves (creates the classpath entries for) the classpath container with
     * the given runtime and the given classpath container id (returned from
     * getClasspathEntryIds()). If the classpath container cannot be resolved
     * (for instance, if the runtime does not exist), return null.
     * 
     * @param project the project to resolve
     * @param runtime the runtime to resolve the container for
     * @return an array of classpath entries for the container, or null if the
     *   container could not be resolved
     */
    public IClasspathEntry[] resolveClasspathContainer(IProject project, IRuntime runtime) {
        return null;
    }

    /**
     * Resolve the classpath container.
     * 
     * @param runtime a runtime
     * @return a possibly empty array of classpath entries
     * @deprecated should use resolveClasspathContainerImpl(IProject, IRuntime) instead
     */
    public IClasspathEntry[] resolveClasspathContainerImpl(IRuntime runtime) {
        return resolveClasspathContainerImpl(null, runtime);
    }

    /**
     * Resolve the classpath container.
     * 
     * @param project a project
     * @param runtime a runtime
     * @return a possibly empty array of classpath entries
     */
    public IClasspathEntry[] resolveClasspathContainerImpl(IProject project, IRuntime runtime) {
        if (runtime == null)
            return new IClasspathEntry[0];
        runtimePathMap.put(runtime.getId(), runtime.getLocation());
        IClasspathEntry[] entries = resolveClasspathContainer(project, runtime);
        if (entries == null)
            entries = resolveClasspathContainer(runtime);

        if (entries == null)
            entries = new IClasspathEntry[0];

        synchronized (this) {
            if (sourceAttachments == null)
                load();
        }
        List<SourceAttachmentUpdate> srcAttachments = sourceAttachments;

        if (srcAttachments != null) {
            int size = entries.length;
            int size2 = srcAttachments.size();
            for (int i = 0; i < size; i++) {
                for (int j = 0; j < size2; j++) {
                    SourceAttachmentUpdate sau = srcAttachments.get(j);
                    if (sau.runtimeId.equals(runtime.getId()) && sau.entry.equals(entries[i].getPath())) {
                        IClasspathAttribute[] consolidatedClasspathAttributes = consolidateClasspathAttributes(
                                sau.attributes, entries[i].getExtraAttributes());
                        entries[i] = JavaCore.newLibraryEntry(entries[i].getPath(), sau.sourceAttachmentPath,
                                sau.sourceAttachmentRootPath, entries[i].getAccessRules(),
                                consolidatedClasspathAttributes, false);
                        break;
                    }
                }
            }
        }

        String key = project.getName() + "/" + runtime.getId();
        if (!previousClasspath.containsKey(key))
            previousClasspath.put(key, new Integer(entries.length));
        else {
            Integer previousEntries = previousClasspath.get(key);

            if ((previousEntries == null) || (previousEntries.intValue() != entries.length)) {
                if (Trace.FINEST) {
                    Trace.trace(Trace.STRING_FINEST, "Classpath update: " + key + " " + entries);
                }
                previousClasspath.put(key, new Integer(entries.length));

                IPath path = new Path(RuntimeClasspathContainer.SERVER_CONTAINER);
                path = path.append(extensionId).append(runtime.getId());
                try {
                    IJavaProject javaProject = JavaCore.create(project);
                    JavaCore.setClasspathContainer(path, new IJavaProject[] { javaProject },
                            new IClasspathContainer[] { null }, new NullProgressMonitor());
                } catch (Exception e) {
                    if (Trace.WARNING) {
                        Trace.trace(Trace.STRING_WARNING, "Error updating classpath", e);
                    }
                }
            }
        }

        return entries;
    }

    /*
     * Returns true if there are any changes in the runtime since the last time that the
     * classpath was resolved which may affect the classpath, and false otherwise. This
     * method is used to check projects when a runtime changes and automatically rebuild
     * them if necessary.
     * 
     * @param runtime a runtime
     * @return <code>true</code> if the classpath may change due to a change in the runtime,
     *    and <code>false</code> if there are no changes
     */
    public boolean hasRuntimeClasspathChanged(IRuntime runtime) {
        try {
            IPath path = runtimePathMap.get(runtime.getId());
            return (path != null && !path.equals(runtime.getLocation()));
        } catch (Exception e) {
            // ignore
        }
        return false;
    }

    private static void addJarFiles(File dir, List<IClasspathEntry> list, boolean includeSubdirectories) {
        int depth = 0;
        if (includeSubdirectories)
            depth = 2;
        addJarFiles(dir, list, depth);
    }

    private static void addJarFiles(File dir, List<IClasspathEntry> list, int depth) {
        if (dir == null)
            throw new IllegalArgumentException();

        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory() && depth > 0) {
                    addJarFiles(file, list, depth - 1);
                } else if (file.getAbsolutePath().endsWith(".jar") || file.getAbsolutePath().endsWith(".zip")) {
                    IPath path = new Path(file.getAbsolutePath());
                    list.add(JavaCore.newLibraryEntry(path, null, null));
                }
            }
        }
    }

    /**
     * Add library entries to the given list for every jar file found in the
     * given directory. Optionally search subdirectories as well.
     * 
     * @param list a list
     * @param dir a directory
     * @param includeSubdirectories <code>true</code> to include subdirectories, and
     *    <code>false</code> otherwise
     */
    protected static void addLibraryEntries(List<IClasspathEntry> list, File dir, boolean includeSubdirectories) {
        if (dir == null)
            throw new IllegalArgumentException();
        addJarFiles(dir, list, includeSubdirectories);
    }

    /**
     * Request that the classpath container for the given runtime and id be updated
     * with the given classpath container entries.
     * 
     * @param runtime a runtime
     * @param entries an array of classpath entries
     */
    public void requestClasspathContainerUpdate(IRuntime runtime, IClasspathEntry[] entries) {
        // default behaviour is to save the source path entries
        if (runtime == null || entries == null)
            return;

        // find the source attachments
        List<SourceAttachmentUpdate> srcAttachments = new ArrayList<SourceAttachmentUpdate>();

        for (IClasspathEntry entry : entries) {
            if (entry.getSourceAttachmentPath() != null
                    || (entry.getExtraAttributes() != null && entry.getExtraAttributes().length > 0)) {
                SourceAttachmentUpdate sau = new SourceAttachmentUpdate();
                sau.runtimeId = runtime.getId();
                sau.entry = entry.getPath();
                sau.sourceAttachmentPath = entry.getSourceAttachmentPath();
                sau.sourceAttachmentRootPath = entry.getSourceAttachmentRootPath();
                sau.attributes = entry.getExtraAttributes();
                srcAttachments.add(sau);
            }
        }
        sourceAttachments = srcAttachments;
        save();
    }

    /**
     * Load source attachment info.
     */
    private void load() {
        List<SourceAttachmentUpdate> srcAttachments = new ArrayList<SourceAttachmentUpdate>();

        String id = extensionId;
        String filename = JavaServerPlugin.getInstance().getStateLocation().append(id + ".xml").toOSString();
        if (!(new File(filename)).exists())
            return;

        try {
            IMemento memento = XMLMemento.loadMemento(filename);

            IMemento[] children = memento.getChildren("source-attachment");
            for (IMemento child : children) {
                try {
                    SourceAttachmentUpdate sau = new SourceAttachmentUpdate();
                    sau.runtimeId = child.getString("runtime-id");
                    String temp = child.getString("entry");
                    if (temp != null)
                        sau.entry = new Path(temp);
                    temp = child.getString("source-attachment-path");
                    if (temp != null)
                        sau.sourceAttachmentPath = new Path(temp);
                    temp = child.getString("source-attachment-root-path");
                    if (temp != null)
                        sau.sourceAttachmentRootPath = new Path(temp);
                    IMemento[] attrChildren = child.getChildren("attribute");
                    if (attrChildren != null) {
                        int size2 = attrChildren.length;
                        sau.attributes = new IClasspathAttribute[size2];
                        for (int j = 0; j < size2; j++) {
                            String name = attrChildren[j].getString("name");
                            String value = attrChildren[j].getString("value");
                            sau.attributes[j] = JavaCore.newClasspathAttribute(name, value);
                        }
                    }
                    srcAttachments.add(sau);
                } catch (Exception e) {
                    if (Trace.WARNING) {
                        Trace.trace(Trace.STRING_WARNING, "Could not load source attachment: " + e);
                    }
                }
            }
        } catch (Exception e) {
            if (Trace.WARNING) {
                Trace.trace(Trace.STRING_WARNING, "Could not load source path info", e);
            }
        }
        sourceAttachments = srcAttachments;
    }

    /**
     * Save source attachment info.
     */
    private synchronized void save() {
        List<SourceAttachmentUpdate> srcAttachments = sourceAttachments;
        if (srcAttachments == null)
            return;
        String id = extensionId;
        String filename = JavaServerPlugin.getInstance().getStateLocation().append(id + ".xml").toOSString();
        try {
            XMLMemento memento = XMLMemento.createWriteRoot("classpath");

            Iterator iterator = srcAttachments.iterator();
            while (iterator.hasNext()) {
                SourceAttachmentUpdate sau = (SourceAttachmentUpdate) iterator.next();
                IMemento child = memento.createChild("source-attachment");
                child.putString("runtime-id", sau.runtimeId);
                if (sau.entry != null)
                    child.putString("entry", sau.entry.toPortableString());
                if (sau.sourceAttachmentPath != null)
                    child.putString("source-attachment-path", sau.sourceAttachmentPath.toPortableString());
                if (sau.sourceAttachmentRootPath != null)
                    child.putString("source-attachment-root-path", sau.sourceAttachmentRootPath.toPortableString());
                if (sau.attributes != null) {
                    for (IClasspathAttribute attr : sau.attributes) {
                        IMemento attrChild = child.createChild("attribute");
                        attrChild.putString("name", attr.getName());
                        attrChild.putString("value", attr.getValue());
                    }
                }
            }

            memento.saveToFile(filename);
        } catch (Exception e) {
            if (Trace.SEVERE) {
                Trace.trace(Trace.STRING_SEVERE, "Error saving source path info", e);
            }
        }
    }

    public IClasspathAttribute[] consolidateClasspathAttributes(IClasspathAttribute[] sourceAttachmentAttributes,
            IClasspathAttribute[] classpathEntryAttributes) {
        List classpathAttributeList = new ArrayList();
        classpathAttributeList.addAll(Arrays.asList(sourceAttachmentAttributes));
        for (int i = 0; i < classpathEntryAttributes.length; i++) {
            boolean attributeCollision = false;
            for (int j = 0; j < sourceAttachmentAttributes.length; j++) {
                String name = classpathEntryAttributes[i].getName();
                if (name != null && name.equals(sourceAttachmentAttributes[j].getName())) {
                    attributeCollision = true;
                    break;
                }
            }
            if (!attributeCollision) {
                classpathAttributeList.add(classpathEntryAttributes[i]);
            }
        }
        return (IClasspathAttribute[]) classpathAttributeList
                .toArray(new IClasspathAttribute[classpathAttributeList.size()]);
    }
}