Package org.jboss.gravia.provision.spi

Source Code of org.jboss.gravia.provision.spi.AbstractProvisioner$DefaultProvisionResult

/*
* #%L
* Gravia :: Provision
* %%
* Copyright (C) 2013 - 2014 JBoss by Red Hat
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.jboss.gravia.provision.spi;

import static org.jboss.gravia.provision.spi.ProvisionLogger.LOGGER;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.jboss.gravia.provision.ProvisionException;
import org.jboss.gravia.provision.ProvisionResult;
import org.jboss.gravia.provision.Provisioner;
import org.jboss.gravia.provision.ResourceHandle;
import org.jboss.gravia.provision.ResourceInstaller;
import org.jboss.gravia.provision.ResourceInstaller.Context;
import org.jboss.gravia.repository.MavenIdentityRequirementBuilder;
import org.jboss.gravia.repository.Repository;
import org.jboss.gravia.resolver.DefaultPreferencePolicy;
import org.jboss.gravia.resolver.DefaultResolveContext;
import org.jboss.gravia.resolver.Environment;
import org.jboss.gravia.resolver.PreferencePolicy;
import org.jboss.gravia.resolver.ResolutionException;
import org.jboss.gravia.resolver.ResolveContext;
import org.jboss.gravia.resolver.Resolver;
import org.jboss.gravia.resolver.spi.AbstractEnvironment;
import org.jboss.gravia.resource.Capability;
import org.jboss.gravia.resource.ContentNamespace;
import org.jboss.gravia.resource.DefaultResourceBuilder;
import org.jboss.gravia.resource.IdentityNamespace;
import org.jboss.gravia.resource.MavenCoordinates;
import org.jboss.gravia.resource.Requirement;
import org.jboss.gravia.resource.Resource;
import org.jboss.gravia.resource.ResourceBuilder;
import org.jboss.gravia.resource.ResourceIdentity;
import org.jboss.gravia.runtime.DefaultWire;
import org.jboss.gravia.runtime.DefaultWiring;
import org.jboss.gravia.runtime.Wire;
import org.jboss.gravia.runtime.Wiring;
import org.jboss.gravia.utils.IllegalArgumentAssertion;
import org.jboss.gravia.utils.IllegalStateAssertion;
import org.jboss.gravia.utils.ResourceUtils;

/**
* An abstract {@link Provisioner}
*
* @author thomas.diesler@jboss.com
* @since 06-May-2013
*/
public abstract class AbstractProvisioner implements Provisioner {

    private final Resolver resolver;
    private final Repository repository;
    private final Environment environment;
    private final ResourceInstaller installer;
    private final PreferencePolicy preferencePolicy;

    public AbstractProvisioner(Environment environment, Resolver resolver, Repository repository, ResourceInstaller installer) {
        this(environment, resolver, repository, installer, new DefaultPreferencePolicy(null));
    }

    public AbstractProvisioner(Environment environment, Resolver resolver, Repository repository, ResourceInstaller installer, PreferencePolicy policy) {
        IllegalArgumentAssertion.assertNotNull(environment, "environment");
        IllegalArgumentAssertion.assertNotNull(resolver, "resolver");
        IllegalArgumentAssertion.assertNotNull(repository, "repository");
        IllegalArgumentAssertion.assertNotNull(installer, "installer");
        IllegalArgumentAssertion.assertNotNull(policy, "policy");
        this.environment = environment;
        this.resolver = resolver;
        this.repository = repository;
        this.installer = installer;
        this.preferencePolicy = policy;
    }

    @Override
    public Environment getEnvironment() {
        return environment;
    }

    @Override
    public final Resolver getResolver() {
        return resolver;
    }

    @Override
    public final Repository getRepository() {
        return repository;
    }

    @Override
    public ResourceInstaller getResourceInstaller() {
        return installer;
    }

    @Override
    public ProvisionResult findResources(Set<Requirement> reqs) {
        return findResources(getEnvironment().cloneEnvironment(), reqs);
    }

    @Override
    public ProvisionResult findResources(Environment env, Set<Requirement> reqs) {
        IllegalArgumentAssertion.assertNotNull(env, "env");
        IllegalArgumentAssertion.assertNotNull(reqs, "reqs");

        LOGGER.debug("START findResources: {}", reqs);

        // Install the unresolved resources into the cloned environment
        List<Resource> unresolved = new ArrayList<Resource>();
        for (Requirement req : reqs) {
            Resource res = req.getResource();
            if (env.getResource(res.getIdentity()) == null) {
                env.addResource(res);
                unresolved.add(res);
            }
        }

        // Find the resources in the cloned environment
        List<Resource> resources = new ArrayList<Resource>();
        Set<Requirement> unstatisfied = new HashSet<Requirement>(reqs);
        Map<Requirement, Resource> mapping = new HashMap<Requirement, Resource>();
        findResources(env, unresolved, mapping, unstatisfied, resources);

        // Remove abstract resources
        Iterator<Resource> itres = resources.iterator();
        while (itres.hasNext()) {
            Resource res = itres.next();
            if (ResourceUtils.isAbstract(res)) {
                itres.remove();
            }
        }

        // Sort the provisioner result
        List<Resource> sorted = new ArrayList<Resource>();
        for (Resource res : resources) {
            sortResultResources(res, mapping, resources, sorted);
        }

        ProvisionResult result = new DefaultProvisionResult(sorted, mapping, env.getWirings(), unstatisfied);
        LOGGER.debug("END findResources");
        LOGGER.debug("  resources: {}", result.getResources());
        LOGGER.debug("  unsatisfied: {}", result.getUnsatisfiedRequirements());

        // Sanity check that we can resolve all result resources
        Set<Resource> mandatory = new LinkedHashSet<Resource>();
        mandatory.addAll(result.getResources());
        try {
            ResolveContext context = new DefaultResolveContext(env, mandatory, null);
            resolver.resolveAndApply(context).entrySet();
        } catch (ResolutionException ex) {
            LOGGER.warn("Cannot resolve provisioner result", ex);
        }

        return result;
    }

    @Override
    public Set<ResourceHandle> provisionResources(Set<Requirement> reqs) throws ProvisionException {

        // Find resources
        Environment env = getEnvironment();
        Environment envclone = env.cloneEnvironment();
        ProvisionResult result = findResources(envclone, reqs);

        Set<Requirement> unsatisfied = result.getUnsatisfiedRequirements();
        if (!unsatisfied.isEmpty()) {
            throw new ProvisionException("Cannot resolve unsatisfied requirements: " + unsatisfied);
        }

        // NOTE: installing resources and updating the wiring is not an atomic operation

        // Install resources
        List<Resource> resources = result.getResources();
        Map<Requirement, Resource> mapping = result.getMapping();
        DefaultInstallerContext context = new DefaultInstallerContext(resources, mapping);
        Set<ResourceHandle> handles = installResources(context);

        // Update the wirings
        updateResourceWiring(env, result);

        return handles;
    }

    @Override
    public void updateResourceWiring(Environment environment, ProvisionResult result) {
        Map<Resource, Wiring> auxwirings = result.getWirings();
        for (Entry<Resource, Wiring> entry : auxwirings.entrySet()) {
            Resource auxres = entry.getKey();
            Resource envres = environment.getResource(auxres.getIdentity());
            DefaultWiring envwiring = new DefaultWiring(envres, null, null);
            Wiring auxwiring = entry.getValue();
            for (Wire auxwire : auxwiring.getProvidedResourceWires(null)) {
                Capability auxcap = auxwire.getCapability();
                Capability envcap = findTargetCapability(auxcap);
                Requirement auxreq = auxwire.getRequirement();
                Requirement envreq = findTargetRequirement(auxreq);
                envwiring.addProvidedWire(new DefaultWire(envreq, envcap));
            }
            for (Wire auxwire : auxwiring.getRequiredResourceWires(null)) {
                Capability auxcap = auxwire.getCapability();
                Capability envcap = findTargetCapability(auxcap);
                Requirement auxreq = auxwire.getRequirement();
                Requirement envreq = findTargetRequirement(auxreq);
                envwiring.addRequiredWire(new DefaultWire(envreq, envcap));
            }
            AbstractEnvironment absenv = AbstractEnvironment.assertAbstractEnvironment(environment);
            absenv.putWiring(envres, envwiring);
        }
    }

    private Set<ResourceHandle> installResources(Context context) throws ProvisionException {
        IllegalArgumentAssertion.assertNotNull(context, "context");
        Set<ResourceHandle> handles = new HashSet<ResourceHandle>();
        for (Resource res : context.getResources()) {
            handles.add(installer.installResource(context, res));
        }
        return Collections.unmodifiableSet(handles);
    }

    @Override
    public ResourceBuilder getContentResourceBuilder(ResourceIdentity identity, InputStream inputStream) {
        IllegalArgumentAssertion.assertNotNull(identity, "identity");
        IllegalArgumentAssertion.assertNotNull(inputStream, "inputStream");
        ResourceBuilder builder = new DefaultResourceBuilder();
        builder.addIdentityCapability(identity);
        builder.addContentCapability(inputStream);
        return builder;
    }

    @Override
    public ResourceBuilder getMavenResourceBuilder(ResourceIdentity identity, MavenCoordinates mavenid) {
        IllegalArgumentAssertion.assertNotNull(mavenid, "mavenid");

        Requirement ireq = new MavenIdentityRequirementBuilder(mavenid).getRequirement();
        Collection<Capability> providers = getRepository().findProviders(ireq);
        IllegalStateAssertion.assertFalse(providers.isEmpty(), "Cannot find providers for requirement: " + ireq);

        Resource mvnres = providers.iterator().next().getResource();

        // Copy the mvn resource to another resource with the given identity
        DefaultResourceBuilder builder = new DefaultResourceBuilder();
        Capability icap = identity != null ? builder.addIdentityCapability(identity) : builder.addIdentityCapability(mavenid);
        icap.getAttributes().put(ContentNamespace.CAPABILITY_MAVEN_IDENTITY_ATTRIBUTE, mavenid);
        for (Capability cap : mvnres.getCapabilities(null)) {
            if (!IdentityNamespace.IDENTITY_NAMESPACE.equals(cap.getNamespace())) {
                builder.addCapability(cap.getNamespace(), cap.getAttributes(), cap.getDirectives());
            }
        }
        for (Requirement req : mvnres.getRequirements(null)) {
            builder.addRequirement(req.getNamespace(), req.getAttributes(), req.getDirectives());
        }

        return builder;
    }

    @Override
    public ResourceHandle installResource(Resource resource) throws ProvisionException {
        return installResourceInternal(resource);
    }

    @Override
    public ResourceHandle installSharedResource(Resource resource) throws ProvisionException {
        if (!ResourceUtils.isShared(resource)) {
            ResourceBuilder builder = new DefaultResourceBuilder().fromResource(resource);
            Capability icap = builder.getMutableResource().getIdentityCapability();
            icap.getAttributes().put(IdentityNamespace.CAPABILITY_SHARED_ATTRIBUTE, Boolean.TRUE);
            resource = builder.getResource();
        }
        return installResourceInternal(resource);
    }

    private synchronized ResourceHandle installResourceInternal(Resource resource) throws ProvisionException {
        IllegalArgumentAssertion.assertNotNull(resource, "resource");
        Context context = new DefaultInstallerContext(resource);
        return installer.installResource(context, resource);
    }

    private Capability findTargetCapability(Capability auxcap) {
        Resource auxres = auxcap.getResource();
        Resource envres = environment.getResource(auxres.getIdentity());
        if (auxres == envres)
            return auxcap;
        for (Capability cap : envres.getCapabilities(null)) {
            boolean nsmatch = cap.getNamespace().equals(auxcap.getNamespace());
            boolean attsmatch = cap.getAttributes().equals(auxcap.getAttributes());
            boolean dirsmatch = cap.getDirectives().equals(auxcap.getDirectives());
            if (nsmatch && attsmatch && dirsmatch)
                return cap;
        }
        throw new IllegalStateException("Cannot find target capability for: " + auxcap);
    }

    private Requirement findTargetRequirement(Requirement auxreq) {
        Resource auxres = auxreq.getResource();
        Resource envres = environment.getResource(auxres.getIdentity());
        if (auxres == envres)
            return auxreq;
        for (Requirement req : envres.getRequirements(null)) {
            boolean nsmatch = req.getNamespace().equals(auxreq.getNamespace());
            boolean attsmatch = req.getAttributes().equals(auxreq.getAttributes());
            boolean dirsmatch = req.getDirectives().equals(auxreq.getDirectives());
            if (nsmatch && attsmatch && dirsmatch)
                return req;
        }
        throw new IllegalStateException("Cannot find target requirement for: " + auxreq);
    }

    // Sort mapping targets higher in the list. This should result in resource installations
    // without dependencies on resources from the same provioner result set.
    private void sortResultResources(Resource res, Map<Requirement, Resource> mapping, List<Resource> resources, List<Resource> result) {
        if (!result.contains(res)) {
            for (Requirement req : res.getRequirements(null)) {
                Resource target = mapping.get(req);
                if (target != null && resources.contains(target)) {
                    sortResultResources(target, mapping, resources, result);
                }
            }
            result.add(res);
        }
    }

    private void findResources(Environment env, List<Resource> unresolved, Map<Requirement, Resource> mapping, Set<Requirement> unstatisfied, List<Resource> resources) {

        // Resolve the unsatisfied reqs in the environment
        resolveInEnvironment(env, unresolved, mapping, unstatisfied, resources);
        if (unstatisfied.isEmpty())
            return;

        boolean envModified = false;
        Set<Resource> installable = new HashSet<Resource>();

        LOGGER.debug("Finding unsatisfied reqs");

        Iterator<Requirement> itun = unstatisfied.iterator();
        while (itun.hasNext()) {
            Requirement req = itun.next();

            // Ignore requirements that are already in the environment
            if (!env.findProviders(req).isEmpty()) {
                continue;
            }

            // Continue if we cannot find a provider for a given requirement
            Capability cap = findProviderInRepository(req);
            if (cap == null) {
                continue;
            }

            installable.add(cap.getResource());
        }

        // Install the resources that match the unsatisfied reqs
        for (Resource res : installable) {
            if (!resources.contains(res)) {
                Collection<Requirement> reqs = res.getRequirements(null);
                LOGGER.debug("Adding %d unsatisfied reqs", reqs.size());
                unstatisfied.addAll(reqs);
                env.addResource(res);
                resources.add(res);
                envModified = true;
            }
        }

        // Recursivly find the missing resources
        if (envModified) {
            findResources(env, unresolved, mapping, unstatisfied, resources);
        }
    }

    private Capability findProviderInRepository(Requirement req) {

        // Find the providers in the repository
        LOGGER.debug("Find in repository: {}", req);
        Collection<Capability> providers = repository.findProviders(req);

        // Remove abstract resources
        if (providers.size() > 1) {
            providers = new ArrayList<Capability>(providers);
            Iterator<Capability> itcap = providers.iterator();
            while (itcap.hasNext()) {
                Capability cap = itcap.next();
                if (ResourceUtils.isAbstract(cap.getResource())) {
                    itcap.remove();
                }
            }
        }

        Capability cap = null;
        if (providers.size() == 1) {
            cap = providers.iterator().next();
            LOGGER.debug(" Found one: {}", cap);
        } else if (providers.size() > 1) {
            List<Capability> sorted = new ArrayList<Capability>(providers);
            preferencePolicy.sort(sorted);
            LOGGER.debug(" Found multiple: {}", sorted);
            cap = sorted.get(0);
        } else {
            LOGGER.debug(" Not found: {}", req);
        }
        return cap;
    }

    private void resolveInEnvironment(Environment env, List<Resource> unresolved, Map<Requirement, Resource> mapping, Set<Requirement> unstatisfied, List<Resource> resources) {
        Set<Resource> mandatory = new LinkedHashSet<Resource>();
        mandatory.addAll(unresolved);
        mandatory.addAll(resources);
        try {
            ResolveContext context = new DefaultResolveContext(env, mandatory, null);
            Set<Entry<Resource, List<Wire>>> wiremap = resolver.resolve(context).entrySet();
            for (Entry<Resource, List<Wire>> entry : wiremap) {
                for (Wire wire : entry.getValue()) {
                    Requirement req = wire.getRequirement();
                    Resource provider = wire.getProvider();
                    mapping.put(req, provider);
                }
            }
            unstatisfied.clear();
        } catch (ResolutionException ex) {
            for (Requirement req : ex.getUnresolvedRequirements()) {
                LOGGER.debug(" unresolved: {}", req);
            }
        }
    }

    static class DefaultProvisionResult implements ProvisionResult {

        private final Map<Resource, Wiring> wirings;
        private final Map<Requirement, Resource> mapping;
        private final Set<Requirement> unsatisfied;
        private final List<Resource> resources;

        DefaultProvisionResult(List<Resource> resources, Map<Requirement, Resource> mapping, Map<Resource, Wiring> wirings, Set<Requirement> unstatisfied) {
            this.resources = resources;
            this.mapping = mapping;
            this.wirings = wirings;
            this.unsatisfied = unstatisfied;
        }

        @Override
        public List<Resource> getResources() {
            return Collections.unmodifiableList(resources);
        }

        @Override
        public Map<Requirement, Resource> getMapping() {
            return Collections.unmodifiableMap(mapping);
        }

        @Override
        public Map<Resource, Wiring> getWirings() {
            return Collections.unmodifiableMap(wirings);
        }

        @Override
        public Set<Requirement> getUnsatisfiedRequirements() {
            return Collections.unmodifiableSet(unsatisfied);
        }
    }
}
TOP

Related Classes of org.jboss.gravia.provision.spi.AbstractProvisioner$DefaultProvisionResult

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.