Package org.jboss.gravia.resolver.spi

Source Code of org.jboss.gravia.resolver.spi.AbstractResolver$ResourceCandidates

/*
* #%L
* JBossOSGi Resolver Felix
* %%
* Copyright (C) 2010 - 2013 JBoss by Red Hat
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program.  If not, see
* <http://www.gnu.org/licenses/lgpl-2.1.html>.
* #L%
*/
package org.jboss.gravia.resolver.spi;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;

import org.jboss.gravia.resolver.ResolutionException;
import org.jboss.gravia.resolver.ResolveContext;
import org.jboss.gravia.resolver.Resolver;
import org.jboss.gravia.resource.Capability;
import org.jboss.gravia.resource.Requirement;
import org.jboss.gravia.resource.Resource;
import org.jboss.gravia.resource.Wire;
import org.jboss.gravia.resource.Wiring;
import org.jboss.gravia.resource.spi.AbstractResource;
import org.jboss.gravia.resource.spi.AbstractWire;
import org.jboss.gravia.resource.spi.AbstractWiring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* An abstract resolver {@link Resolver}.
*
* The resolver maintains order on all levels.
* This should guarantee reproducable results.
*
* @author thomas.diesler@jboss.com
* @since 31-May-2010
*/
public abstract class AbstractResolver implements Resolver {

    static final Logger LOGGER = LoggerFactory.getLogger(Resolver.class.getPackage().getName());

    protected abstract AbstractWire createWire(Requirement req, Capability cap);

    protected abstract AbstractWiring createWiring(Resource resource, List<Wire> reqwires, List<Wire> provwires);

    @Override
    public Map<Resource, List<Wire>> resolve(ResolveContext context) throws ResolutionException {
        return resolveInternal(context, false);
    }

    @Override
    public Map<Resource, List<Wire>> resolveAndApply(ResolveContext context) throws ResolutionException {
        return resolveInternal(context, true);
    }

    ResourceSpaces createResourceSpaces(ResolveContext context) {
        return new ResourceSpaces(context);
    }

    ResourceCandidates createResourceCandidates(Resource res) {
        return new ResourceCandidates(res);
    }

    private Map<Resource, List<Wire>> resolveInternal(ResolveContext context, boolean apply) throws ResolutionException {

        LOGGER.debug("Resolve: mandatory{} optional{}", context.getMandatoryResources(), context.getOptionalResources());

        // Get the combined set of resources in the context
        Set<Resource> combined = new LinkedHashSet<Resource>();
        combined.addAll(context.getMandatoryResources());
        combined.addAll(context.getOptionalResources());

        // Resolve combined resources
        ResolverState state = new ResolverState(context);
        for (Resource res : combined) {
            resolveResource(state, res);
        }

        // Log resolver result
        Map<Resource, List<Wire>> resourceWires = state.getResult();
        if (LOGGER.isDebugEnabled()) {
            for (Entry<Resource, List<Wire>> entry : resourceWires.entrySet()) {
                LOGGER.debug("Resolved: {}", entry.getKey());
                for (Wire wire : entry.getValue()) {
                    LOGGER.debug("   {}", wire);
                }
            }
        }

        // Apply resolver results
        if (apply) {
            for (Entry<Resource, List<Wire>> entry : resourceWires.entrySet()) {
                AbstractResource requirer = (AbstractResource) entry.getKey();
                List<Wire> reqwires = entry.getValue();
                AbstractWiring reqwiring = (AbstractWiring) requirer.getWiring();
                if (reqwiring == null) {
                    reqwiring = createWiring(requirer, reqwires, null);
                    requirer.setWiring(reqwiring);
                } else {
                    for (Wire wire : reqwires) {
                        reqwiring.addRequiredWire(wire);
                    }
                }
                for (Wire wire : reqwires) {
                    AbstractResource provider = (AbstractResource) wire.getProvider();
                    AbstractWiring provwiring = (AbstractWiring) provider.getWiring();
                    if (provwiring == null) {
                        provwiring = createWiring(provider, null, null);
                        provider.setWiring(provwiring);
                    }
                    provwiring.addProvidedWire(wire);
                }
            }
        }

        return resourceWires;
    }

    private ResourceSpace resolveResource(ResolverState state, Resource res) throws ResolutionException {

        // Check if we already have a resolved space for resource
        ResourceSpaces spaces = state.getResourceSpaces();
        ResourceSpace resspace = spaces.getResourceSpace(res);
        if (resspace != null)
            return resspace;

        // A resource can resolve when the spaces of all its immediate dependencies can be added
        ResourceCandidates rescan = new ResourceCandidates(res);
        Iterator<List<Wire>> itres = rescan.iterator(state.getResolveContext());
        while (itres.hasNext()) {
            ResourceSpace space = new ResourceSpace(res);
            List<Wire> wires = itres.next();
            boolean allgood = true;
            for (Wire wire : wires) {
                Resource provider = wire.getProvider();
                ResourceSpace provspace = resolveResource(state, provider);
                if (!space.addDependencySpace(provspace)) {
                    allgood = false;
                    break;
                }
            }
            if (allgood) {
                spaces.addResourceSpace(space);
                state.getResult().put(res, wires);
                return space;
            }
        }

        ResolutionException resex = rescan.getResolutionException();
        if (resex != null) {
            throw resex;
        }

        if (spaces.getResourceSpace(res) == null) {
            List<Requirement> manreqs = res.getRequirements(null);
            Iterator<Requirement> itreqs = manreqs.iterator();
            while (itreqs.hasNext()) {
                if (itreqs.next().isOptional()) {
                    itreqs.remove();
                }
            }
            throw new ResolutionException("Requirements map to candidates in disconnetced spaces", null, manreqs);
        }
        return null;
    }

    private class ResolverState {

        private final ResourceSpaces spaces;
        private final ResolveContext context;
        private final Map<Resource, List<Wire>> wiremap = new LinkedHashMap<Resource, List<Wire>>();

        ResolverState(ResolveContext context) {
            this.context = context;
            this.spaces = new ResourceSpaces(context);
        }

        ResourceSpaces getResourceSpaces() {
            return spaces;
        }

        ResolveContext getResolveContext() {
            return context;
        }

        Map<Resource, List<Wire>> getResult() {
            return wiremap;
        }
    }

    class ResourceSpaces {

        private final Map<Resource, ResourceSpace> spacemap = new LinkedHashMap<Resource, ResourceSpace>();

        // Initially contain spaces for all wired resources
        ResourceSpaces(ResolveContext context) {
            for (Resource res : context.getWirings().keySet()) {
                spacemap.put(res, new ResourceSpace(res));
            }
        }

        ResourceSpaces(ResourceSpaces parent) {
            spacemap.putAll(parent.spacemap);
        }

        void addResourceSpace(ResourceSpace space) {
            Resource primary = space.getPrimary();
            assert !spacemap.containsKey(primary) : "spaces does not contain: " + primary;
            spacemap.put(primary, space);
        }

        Map<Resource, ResourceSpace> getResourceSpaces() {
            return Collections.unmodifiableMap(spacemap);
        }

        ResourceSpace getResourceSpace(Resource res) {
            return spacemap.get(res);
        }
    }

    /**
     * A {@link ResourceSpace} is that of a primary {@link Resource} and its immediate dependencies.
     * A space cannot contain two versions of the same {@link Resource}.
     */
    class ResourceSpace {

        private final Resource primary;
        private final Map<String, Resource> resources = new LinkedHashMap<String, Resource>();

        ResourceSpace(Resource primary) {
            this.primary = primary;

            String uniquekey = primary.getIdentity().getSymbolicName();
            resources.put(uniquekey, primary);

            Wiring wiring = primary.getWiring();
            if (wiring != null) {
                for (Wire wire : wiring.getRequiredResourceWires(null)) {
                    Resource provider = wire.getProvider();
                    uniquekey = provider.getIdentity().getSymbolicName();
                    resources.put(uniquekey, provider);
                }
            }
        }

        Resource getPrimary() {
            return primary;
        }

        Collection<Resource> getResources() {
            return Collections.unmodifiableCollection(resources.values());
        }

        boolean addDependencySpace(ResourceSpace dependency) {
            if (dependency == null)
                return false;

            for (Resource aux : dependency.getResources()) {
                String uniquekey = aux.getIdentity().getSymbolicName();
                Resource other = resources.get(uniquekey);
                if (other != null && other != aux) {
                    return false;
                }
            }
            for (Resource aux : dependency.getResources()) {
                String uniquekey = aux.getIdentity().getSymbolicName();
                resources.put(uniquekey, aux);
            }
            return true;
        }

        @Override
        public String toString() {
            return "ResourceSpace[" + primary.getIdentity() + "]";
        }
    }

    /**
     * Provides an iterator over all possible wires for a given resource
     */
    class ResourceCandidates {
        private final Resource res;
        private ResolutionException resolutionException;

        ResourceCandidates(Resource res) {
            this.res = res;
        }

        ResolutionException getResolutionException() {
            return resolutionException;
        }

        Iterator<List<Wire>> iterator(final ResolveContext context) throws ResolutionException {
            return new Iterator<List<Wire>>() {
                private List<Iterator<Wire>> candidates = new ArrayList<Iterator<Wire>>();
                private List<Requirement> reqs = res.getRequirements(null);
                private Map<Requirement, Wire> wires;
                private boolean hasNext;

                @Override
                public boolean hasNext() {
                    try {
                        if (wires == null) {
                            wires = new LinkedHashMap<Requirement, Wire>();
                            initWiremap(context, 0);
                            hasNext = true;
                            return true;
                        }
                        int index = reqs.size() - 1;
                        while (index >= 0) {
                            if (candidates.get(index).hasNext()) {
                                Wire wire = candidates.get(index).next();
                                Requirement req = wire.getRequirement();
                                wires.put(req, wire);
                                hasNext = true;
                                return true;
                            } else {
                                if (index > 0 && candidates.get(index - 1).hasNext()) {
                                    initWiremap(context, index);
                                }
                                index--;
                            }
                        }
                    } catch (ResolutionException ex) {
                        resolutionException = ex;
                    }
                    hasNext = false;
                    return false;
                }

                private void initWiremap(final ResolveContext context, int start) throws ResolutionException {
                    for (int i = start; i < reqs.size(); i++) {
                        Requirement req = reqs.get(i);
                        RequirementCandidates reqcan = new RequirementCandidates(req);
                        Iterator<Wire> itcan = reqcan.iterator(context);
                        if (start == 0) {
                            candidates.add(itcan);
                        } else {
                            candidates.set(i, itcan);
                        }
                        if (itcan.hasNext()) {
                            wires.put(req, itcan.next());
                        }
                    }
                }

                @Override
                public List<Wire> next() {
                    if (!hasNext)
                        throw new NoSuchElementException();
                    ArrayList<Wire> next = new ArrayList<Wire>(wires.values());
                    return Collections.unmodifiableList(next);
                }

                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
        }
    }

    /**
     * Provides an iterator over all possible wires for a given requirement
     */
    class RequirementCandidates {
        private final Requirement req;

        RequirementCandidates(Requirement req) {
            this.req = req;
        }

        Iterator<Wire> iterator(ResolveContext context) throws ResolutionException {
            final Collection<Resource> optres = context.getOptionalResources();
            final List<Capability> providers = context.findProviders(req);

            // Fail early if there are no providers for a non-optional requirement
            if (!optres.contains(req.getResource()) && !req.isOptional() && providers.isEmpty()) {
                Set<Requirement> unresolved = Collections.singleton(req);
                throw new ResolutionException("Cannot find provider for: " + req, null, unresolved);
            }

            return new Iterator<Wire>() {
                Iterator<Capability> delagate = providers.iterator();

                @Override
                public boolean hasNext() {
                    return delagate.hasNext();
                }

                @Override
                public Wire next() {
                    Capability cap = delagate.next();
                    return createWire(req, cap);
                }

                @Override
                public void remove() {
                    throw new UnsupportedOperationException();
                }
            };
        }
    }
}
TOP

Related Classes of org.jboss.gravia.resolver.spi.AbstractResolver$ResourceCandidates

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.