org.eclipse.virgo.ide.module.core.ServerModuleDelegate.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.virgo.ide.module.core.ServerModuleDelegate.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2010 SpringSource, a divison of VMware, Inc.
 * 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:
 *     SpringSource, a division of VMware, Inc. - initial API and implementation
 *     GianMaria Romanato - support for nested plans
 *******************************************************************************/

package org.eclipse.virgo.ide.module.core;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.lang.ObjectUtils;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jdt.core.IClasspathAttribute;
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.ui.statushandlers.StatusManager;
import org.eclipse.virgo.ide.facet.core.FacetCorePlugin;
import org.eclipse.virgo.ide.facet.core.FacetUtils;
import org.eclipse.virgo.ide.manifest.core.BundleManifestCorePlugin;
import org.eclipse.virgo.ide.par.Bundle;
import org.eclipse.virgo.ide.par.Par;
import org.eclipse.virgo.util.osgi.manifest.BundleManifest;
import org.eclipse.wst.server.core.IModule;
import org.eclipse.wst.server.core.ServerUtil;
import org.eclipse.wst.server.core.model.IModuleFile;
import org.eclipse.wst.server.core.model.IModuleFolder;
import org.eclipse.wst.server.core.model.IModuleResource;
import org.eclipse.wst.server.core.model.ModuleDelegate;
import org.eclipse.wst.server.core.util.ModuleFile;
import org.eclipse.wst.server.core.util.ModuleFolder;
import org.eclipse.wst.server.core.util.ProjectModule;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * {@link ProjectModule} extension that knows how to handle par and bundle projects.
 *
 * @author Christian Dupuis
 * @author Terry Hon
 * @since 1.0.0
 */
@SuppressWarnings({ "deprecation", "restriction" })
public class ServerModuleDelegate extends ProjectModule {

    /** Make */
    public static final String TEST_CLASSPATH_ENTRY_ATTRIBUTE = "org.eclipse.virgo.ide.jdt.core.test.classpathentry";

    public ServerModuleDelegate(IProject project) {
        super(project);
    }

    @Override
    public IModuleResource[] members() throws CoreException {

        IModule module = getModule();
        final Set<IModuleResource> resources = new LinkedHashSet<IModuleResource>();

        /*
         * Add recursion to collect nested elements. This method now returns as members of toplevel plans all nested
         * plans and bundles collected via recursion.
         *
         * Originally it was returning only direct children and assuming they were bundles, which made it impossible for
         * the tools to deploy nested plans.
         *
         * Note that when top level plans with nested plans are added to the virgo runtime, they are added as a tree of
         * IModule objects. However, apparently WTP does not properly deal with arbitrary nesting of modules and just
         * publishes resources for the top level modules and their direct children.
         *
         * As such to overcome this limitation the following code fools WTP by providing as resources of the top level
         * plan all resources contained in nested plans and nested bundles, even if they are represented by different
         * IModule instances in memory.
         */
        deepGetMembers(module, resources);

        return resources.toArray(new IModuleResource[resources.size()]);
    }

    private void deepGetMembers(IModule module, final Set<IModuleResource> resources)
            throws CoreException, JavaModelException {
        IPath moduleRelativePath = Path.EMPTY;
        // Handle simple case of project being a bundle first

        if (module.getModuleType().getId().equals(FacetCorePlugin.BUNDLE_FACET_ID)) {
            if (FacetUtils.hasProjectFacet(getProject(), FacetCorePlugin.WEB_FACET_ID)) {
                IModule[] modules = ServerUtil.getModules(getProject());
                for (IModule webModule : modules) {
                    if (webModule.getModuleType().getId().equals(FacetCorePlugin.WEB_FACET_ID)) {
                        ModuleDelegate delegate = (ModuleDelegate) webModule.loadAdapter(ModuleDelegate.class,
                                null);
                        resources.addAll(Arrays.asList(delegate.members()));
                    }
                }
            }
            resources.addAll(getMembers(getProject(), moduleRelativePath));
        }
        // More complex handling of PAR and nested bundle project
        else if (module.getModuleType().getId().equals(FacetCorePlugin.PAR_FACET_ID)) {

            // Get the META-INF folder of the PAR first
            IResource metaInfFolder = getProject().findMember(BundleManifestCorePlugin.MANIFEST_FOLDER_NAME);
            if (metaInfFolder instanceof IContainer) {
                String moduleFolderName = BundleManifestCorePlugin.MANIFEST_FOLDER_NAME;
                moduleRelativePath = new Path(moduleFolderName);
                ModuleFolder folder = new ModuleFolder(null, moduleFolderName, Path.EMPTY);
                folder.setMembers(getModuleResources(moduleRelativePath, (IContainer) metaInfFolder));
                resources.add(folder);
            } else {
                StatusManager.getManager().handle(new Status(IStatus.ERROR, BundleManifestCorePlugin.PLUGIN_ID,
                        "Cannot find META-INF/MANIFEST.MF in project [" + getProject().getName() + "]"));
            }

            // Find linked or nested jars and add them to the deployment
            getProject().accept(new IResourceVisitor() {

                public boolean visit(IResource resource) throws CoreException {
                    if (resource instanceof IFile && resource.getFileExtension().equals("jar")) {
                        resources.add(new ModuleFile((IFile) resource, resource.getName(), Path.EMPTY));
                    }
                    return true;
                }
            }, IResource.DEPTH_ONE, false);

            // Iterate nested bundle projects
            for (IModule childModule : getChildModules()) {

                // Special handling of par nested wars with bundle nature
                if (FacetUtils.hasProjectFacet(childModule.getProject(), FacetCorePlugin.WEB_FACET_ID)) {
                    moduleRelativePath = new Path(childModule.getProject().getName() + ".war");
                    ModuleDelegate delegate = (ModuleDelegate) childModule.loadAdapter(ModuleDelegate.class, null);

                    IModuleResource[] members = delegate.members();
                    for (IModuleResource member : members) {
                        if (member instanceof IModuleFile) {
                            resources.add(new ParModuleFile((IModuleFile) member, moduleRelativePath));
                        } else if (member instanceof IModuleFolder) {
                            resources.add(new ParModuleFolder((IModuleFolder) member, moduleRelativePath));
                        }
                    }
                }
                // All other bundles project nested in a par
                else if (FacetUtils.isBundleProject(childModule.getProject())) {
                    String moduleFolderName = childModule.getProject().getName() + ".jar";
                    moduleRelativePath = new Path(moduleFolderName);
                    ModuleFolder folder = new ModuleFolder(null, moduleFolderName, Path.EMPTY);
                    folder.setMembers(getMembers(childModule.getProject(), moduleRelativePath)
                            .toArray(new IModuleResource[0]));
                    resources.add(folder);
                }
            }
        }
        // handling for plan projects
        else if (module.getModuleType().getId().equals(FacetCorePlugin.PLAN_FACET_ID)) {

            // Get the plan file
            String fileName = module.getId();
            fileName = fileName.substring(fileName.indexOf(':') + 1);
            IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fileName));
            if (!file.exists()) {
                return;
            }

            ModuleFile planFile = new ModuleFile(file, file.getName(), moduleRelativePath);
            resources.add(planFile);

            // Iterate nested bundle projects
            ModuleDelegate delegate0 = (ModuleDelegate) module.loadAdapter(ModuleDelegate.class, null);
            for (IModule childModule : delegate0.getChildModules()) {

                // Special handling of par nested wars with bundle nature
                if (FacetUtils.hasProjectFacet(childModule.getProject(), FacetCorePlugin.WEB_FACET_ID)) {
                    moduleRelativePath = new Path(childModule.getProject().getName() + ".war");
                    ModuleDelegate delegate1 = (ModuleDelegate) childModule.loadAdapter(ModuleDelegate.class, null);

                    IModuleResource[] members = delegate1.members();
                    for (IModuleResource member : members) {
                        if (member instanceof IModuleFile) {
                            resources.add(new ParModuleFile((IModuleFile) member, moduleRelativePath));
                        } else if (member instanceof IModuleFolder) {
                            resources.add(new ParModuleFolder((IModuleFolder) member, moduleRelativePath));
                        }
                    }
                }
                // All other bundles project nested in a par
                else if (FacetUtils.isBundleProject(childModule.getProject())) {
                    String moduleFolderName = childModule.getProject().getName() + ".jar";
                    moduleRelativePath = new Path(moduleFolderName);
                    ModuleFolder folder = new ModuleFolder(null, moduleFolderName, Path.EMPTY);
                    folder.setMembers(getMembers(childModule.getProject(), moduleRelativePath)
                            .toArray(new IModuleResource[0]));
                    resources.add(folder);
                } else if (FacetUtils.isPlanProject(childModule.getProject())) {
                    // enter recursion
                    deepGetMembers(childModule, resources);
                } else if (FacetUtils.isParProject(childModule.getProject())) {
                    moduleRelativePath = new Path(childModule.getProject().getName() + ".par");
                    ModuleDelegate delegate2 = (ModuleDelegate) childModule.loadAdapter(ModuleDelegate.class, null);

                    IModuleResource[] members = delegate2.members();
                    for (IModuleResource member : members) {
                        if (member instanceof IModuleFile) {
                            resources.add(new ParModuleFile((IModuleFile) member, moduleRelativePath));
                        } else if (member instanceof IModuleFolder) {
                            resources.add(new ParModuleFolder((IModuleFolder) member, moduleRelativePath));
                        }
                    }
                }
            }

        }
    }

    /**
     * Get all resources from project's output locations
     */
    private Set<IModuleResource> getMembers(IProject project, IPath moduleRelativePath)
            throws JavaModelException, CoreException {
        Set<IModuleResource> resources = new LinkedHashSet<IModuleResource>();
        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
        IJavaProject javaProject = JavaCore.create(project);

        // Add default output location
        IResource defaultBinFolder = root.findMember(javaProject.getOutputLocation());
        if (defaultBinFolder instanceof IContainer) {
            resources.addAll(Arrays.asList(getModuleResources(moduleRelativePath, (IContainer) defaultBinFolder)));
        }

        // Add output for every source entry
        for (IClasspathEntry entry : getSourceClasspathEntries(project, false)) {
            IResource binFolder = root.findMember(entry.getOutputLocation());
            if (binFolder instanceof IContainer && !(binFolder instanceof IWorkspaceRoot)) {
                resources.addAll(
                        Arrays.asList(getModuleResources(moduleRelativePath, (IContainer) defaultBinFolder)));
            }
        }

        // Add Bundle-ClassPath entries
        BundleManifest manifest = BundleManifestCorePlugin.getBundleManifestManager()
                .getBundleManifest(javaProject);
        if (manifest != null) {
            List<String> bundleClassPathEntries = manifest.getBundleClasspath();
            if (bundleClassPathEntries != null) {
                // remove the . for the class folder from the bundle classpath entries
                bundleClassPathEntries.remove(".");

                // get all resources that match the given Bundle-ClassPath header
                resources.addAll(Arrays.asList(
                        getModuleResources(moduleRelativePath, javaProject.getProject(), bundleClassPathEntries)));
            }
        }
        return resources;
    }

    /**
     * Gets all resources of the given <code>container</code> but filters against names given by <code>filters</code>.
     */
    protected IModuleResource[] getModuleResources(IPath path, IContainer container, List<String> filters)
            throws CoreException {
        IResource[] resources = container.members();
        if (resources != null) {
            int size = resources.length;
            List<IModuleResource> list = new ArrayList<IModuleResource>(size);
            for (int i = 0; i < size; i++) {
                IResource resource = resources[i];
                if (resource != null && resource.exists()) {
                    String name = resource.getName();
                    String relativePath = resource.getProjectRelativePath().toString();
                    if (resource instanceof IContainer) {
                        for (String filter : filters) {
                            if (filter.trim().startsWith(relativePath)) {
                                IContainer container2 = (IContainer) resource;
                                ModuleFolder mf = new org.eclipse.wst.server.core.internal.ModuleFolder(container2,
                                        name, path);
                                mf.setMembers(getModuleResources(path.append(name), container2, filters));
                                list.add(mf);
                                break;
                            }
                        }
                    } else if (resource instanceof IFile) {
                        for (String filter : filters) {
                            if (relativePath.equals(filter.trim())) {
                                list.add(new ModuleFile((IFile) resource, name, path));
                                break;
                            }
                        }
                    }
                }
            }
            IModuleResource[] moduleResources = new IModuleResource[list.size()];
            list.toArray(moduleResources);
            return moduleResources;
        }
        return new IModuleResource[0];
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized IModule[] getChildModules() {
        if (FacetUtils.isParProject(getProject())) {
            Set<IModule> modules = new LinkedHashSet<IModule>();

            Par par = FacetUtils.getParDefinition(getProject());
            if (par != null && par.getBundle() != null) {
                for (Bundle bundle : par.getBundle()) {
                    IProject bundleProject = ResourcesPlugin.getWorkspace().getRoot()
                            .getProject(bundle.getSymbolicName());
                    if (FacetUtils.isBundleProject(bundleProject)) {
                        for (IModule module : ServerUtil.getModules(bundleProject)) {
                            if (module.getId().equals(ServerModuleFactoryDelegate.MODULE_FACTORY_ID + ":"
                                    + getProject().getName() + "$" + bundleProject.getName())) {
                                modules.add(module);
                            }
                        }
                    }
                }
            }
            return modules.toArray(new IModule[modules.size()]);
        } else if (FacetUtils.isPlanProject(getProject())) {
            String fileName = getModule().getId();
            fileName = fileName.substring(fileName.indexOf(':') + 1);
            IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fileName));
            if (!file.exists()) {
                return new IModule[0];
            }

            return getPlanDependencies(file).toArray(new IModule[0]);

        }
        return new IModule[0];
    }

    /**
     * {@link IModuleFile} implementation that wraps another {@link IModuleFile} but moves the relative path into a sub
     * directory of a nested par module.
     */
    static class ParModuleFile implements IModuleFile {

        private final IModuleFile wrappedFile;

        private final IPath modulePath;

        public ParModuleFile(IModuleFile wrappedFile, IPath modulePath) {
            this.wrappedFile = wrappedFile;
            this.modulePath = modulePath;
        }

        public long getModificationStamp() {
            return this.wrappedFile.getModificationStamp();
        }

        public IPath getModuleRelativePath() {
            return this.modulePath.append(this.wrappedFile.getModuleRelativePath());
        }

        public String getName() {
            return this.wrappedFile.getName();
        }

        @SuppressWarnings("unchecked")
        public Object getAdapter(Class adapter) {
            return this.wrappedFile.getAdapter(adapter);
        }

        @Override
        public int hashCode() {
            return this.modulePath.hashCode() * 37 + this.wrappedFile.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof ParModuleFile)) {
                return false;
            }
            ParModuleFile other = (ParModuleFile) obj;
            if (!ObjectUtils.equals(this.modulePath, other.modulePath)) {
                return false;
            }
            return ObjectUtils.equals(this.wrappedFile, other.wrappedFile);
        }

        @Override
        public String toString() {
            return "ModuleFile [" + this.modulePath + "/" + this.wrappedFile.getModuleRelativePath() + ", "
                    + this.wrappedFile.getName() + ", " + this.wrappedFile.getModificationStamp() + "]";
        }

    }

    /**
     * {@link IModuleFolder} implementation that wraps another {@link IModuleFolder} but moves the relative path into a
     * sub directory of a nested par module.
     *
     * @see ParModuleFolder#members()
     */
    static class ParModuleFolder implements IModuleFolder {

        private final IModuleFolder wrappedFolder;

        private final IPath modulePath;

        public ParModuleFolder(IModuleFolder wrappedFolder, IPath modulePath) {
            this.wrappedFolder = wrappedFolder;
            this.modulePath = modulePath;
        }

        public IModuleResource[] members() {
            Set<IModuleResource> members = new LinkedHashSet<IModuleResource>();
            for (IModuleResource resource : this.wrappedFolder.members()) {
                if (resource instanceof IModuleFile) {
                    members.add(new ParModuleFile((IModuleFile) resource, this.modulePath));
                } else if (resource instanceof IModuleFolder) {
                    members.add(new ParModuleFolder((IModuleFolder) resource, this.modulePath));
                }
            }
            return members.toArray(new IModuleResource[members.size()]);
        }

        public IPath getModuleRelativePath() {
            return this.modulePath.append(this.wrappedFolder.getModuleRelativePath());
        }

        public String getName() {
            return this.wrappedFolder.getName();
        }

        @SuppressWarnings("unchecked")
        public Object getAdapter(Class adapter) {
            return this.wrappedFolder.getAdapter(adapter);
        }

        @Override
        public int hashCode() {
            return this.modulePath.hashCode() * 37 + this.wrappedFolder.hashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (!(obj instanceof ParModuleFolder)) {
                return false;
            }
            ParModuleFolder other = (ParModuleFolder) obj;
            if (!ObjectUtils.equals(this.modulePath, other.modulePath)) {
                return false;
            }
            return ObjectUtils.equals(this.wrappedFolder, other.wrappedFolder);
        }

        @Override
        public String toString() {
            return "ModuleFile [" + this.modulePath + "/" + this.wrappedFolder.getModuleRelativePath() + ", "
                    + this.wrappedFolder.getName() + "]";
        }
    }

    public static Set<IClasspathEntry> getSourceClasspathEntries(IProject project, boolean onlyTestFolders) {
        IJavaProject javaProject = JavaCore.create(project);
        if (javaProject == null) {
            return Collections.emptySet();
        }
        Set<IClasspathEntry> entries = new LinkedHashSet<IClasspathEntry>();
        try {
            for (IClasspathEntry entry : javaProject.getRawClasspath()) {
                if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
                    if (onlyTestFolders && !isSourceFolder(entry.getExtraAttributes())
                            || !onlyTestFolders && isSourceFolder(entry.getExtraAttributes())) {
                        entries.add(entry);
                    }
                }
            }
        } catch (JavaModelException e) {
        }
        return entries;
    }

    private static boolean isSourceFolder(IClasspathAttribute[] extraAttributes) {
        for (IClasspathAttribute attribute : extraAttributes) {
            if (TEST_CLASSPATH_ENTRY_ATTRIBUTE.equals(attribute.getName())) {
                return !Boolean.valueOf(attribute.getValue());
            }
        }
        return true;
    }

    public Set<IModule> getPlanDependencies(IFile file) {
        if (file == null || !file.exists()) {
            return Collections.emptySet();
        }

        Set<IModule> modules = new HashSet<IModule>();

        /* add recursion to collect nested plans */
        getPlanDependencies0(file, modules);

        return modules;
    }

    private void getPlanDependencies0(IFile file, Set<IModule> modules) {
        if (file == null || !file.exists()) {
            return;
        }

        try {
            DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = docBuilder.parse(file.getContents(true));
            NodeList artifactNodes = doc.getDocumentElement().getElementsByTagName("artifact");
            for (int i = 0; i < artifactNodes.getLength(); i++) {
                Element artifact = (Element) artifactNodes.item(i);
                String type = artifact.getAttribute("type");
                String name = artifact.getAttribute("name");

                if ("bundle".equals(type)) {
                    IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
                    for (IProject candidate : projects) {

                        if (FacetUtils.isBundleProject(candidate)
                                || FacetUtils.hasProjectFacet(candidate, FacetCorePlugin.WEB_FACET_ID)) {

                            BundleManifest manifest = BundleManifestCorePlugin.getBundleManifestManager()
                                    .getBundleManifest(JavaCore.create(candidate));

                            if (manifest != null && manifest.getBundleSymbolicName() != null
                                    && manifest.getBundleSymbolicName().getSymbolicName() != null
                                    && manifest.getBundleSymbolicName().getSymbolicName().equals(name)
                                    || candidate.getName().equals(name)) {
                                for (IModule module : ServerUtil.getModules(candidate)) {
                                    if (!module.getId().contains("$")) {
                                        modules.add(module);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                } else if ("plan".equals(type)) {
                    List<IFile> nested = FacetUtils.getNestedPlanFiles(file, false);
                    for (IFile iFile : nested) {
                        IModule[] mmmm = ServerUtil.getModules(iFile.getProject());
                        for (IModule iModule : mmmm) {
                            String fileName = iModule.getId();
                            fileName = fileName.substring(fileName.indexOf(':') + 1);
                            IFile file2 = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(fileName));
                            if (iFile.equals(file2)) {
                                modules.add(iModule);
                                break;
                            }
                        }

                    }
                } else if ("par".equals(type)) {
                    IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
                    for (IProject candidate : projects) {

                        if (FacetUtils.isParProject(candidate)) {

                            if (candidate.getName().equals(name)) {
                                for (IModule module : ServerUtil.getModules(candidate)) {
                                    modules.add(module);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        } catch (SAXException e) {
            StatusManager.getManager().handle(new Status(IStatus.ERROR, "Problem while getting plan dependencies.",
                    BundleManifestCorePlugin.PLUGIN_ID, e));
        } catch (IOException e) {
            StatusManager.getManager().handle(new Status(IStatus.ERROR, "Problem while getting plan dependencies.",
                    BundleManifestCorePlugin.PLUGIN_ID, e));
        } catch (CoreException e) {
            StatusManager.getManager().handle(e, BundleManifestCorePlugin.PLUGIN_ID);
        } catch (ParserConfigurationException e) {
            StatusManager.getManager().handle(new Status(IStatus.ERROR, "Problem while getting plan dependencies.",
                    BundleManifestCorePlugin.PLUGIN_ID, e));
        }

    }
}