/* * 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. */ package org.apache.aries.subsystem.core.internal; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.aries.subsystem.core.archive.DeploymentManifest; import org.apache.aries.subsystem.core.internal.BundleResourceInstaller.BundleConstituent; import org.apache.aries.util.io.IOUtils; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.wiring.BundleRevision; import org.osgi.resource.Resource; import org.osgi.service.coordinator.Coordination; import org.osgi.service.subsystem.Subsystem; import org.osgi.service.subsystem.Subsystem.State; import org.osgi.service.subsystem.SubsystemException; public class Subsystems { private BasicSubsystem root; private volatile SubsystemGraph graph; private final Map<Long, BasicSubsystem> idToSubsystem = new HashMap<Long, BasicSubsystem>(); private final Map<String, BasicSubsystem> locationToSubsystem = new HashMap<String, BasicSubsystem>(); private final ResourceReferences resourceReferences = new ResourceReferences(); private final Map<BasicSubsystem, Set<Resource>> subsystemToConstituents = new HashMap<BasicSubsystem, Set<Resource>>(); public void addChild(BasicSubsystem parent, BasicSubsystem child, boolean referenceCount) { graph.add(parent, child); child.addedParent(parent, referenceCount); } public void addConstituent(BasicSubsystem subsystem, Resource constituent, boolean referenced) { synchronized (subsystemToConstituents) { Set<Resource> constituents = subsystemToConstituents.get(subsystem); if (constituents == null) { constituents = new HashSet<Resource>(); subsystemToConstituents.put(subsystem, constituents); } constituents.add(constituent); } subsystem.addedConstituent(constituent, referenced); } public void addReference(BasicSubsystem subsystem, Resource resource) { resourceReferences.addReference(subsystem, resource); } public void addSubsystem(BasicSubsystem subsystem) { synchronized (idToSubsystem) { synchronized (locationToSubsystem) { addIdToSubsystem(subsystem); addLocationToSubsystem(subsystem); } } } public Collection<Subsystem> getChildren(BasicSubsystem parent) { return graph.getChildren(parent); } public Collection<Resource> getConstituents(BasicSubsystem subsystem) { synchronized (subsystemToConstituents) { Collection<Resource> result = subsystemToConstituents.get(subsystem); if (result == null) return Collections.emptyList(); return Collections.unmodifiableCollection(new ArrayList<Resource>(result)); } } public Collection<Subsystem> getParents(BasicSubsystem child) { return graph.getParents(child); } public Collection<Resource> getResourcesReferencedBy(BasicSubsystem subsystem) { return resourceReferences.getResources(subsystem); } public synchronized BasicSubsystem getRootSubsystem() { if (root == null) { File file = Activator.getInstance().getBundleContext().getDataFile(""); File[] fileArray = file.listFiles(); List<File> fileList = new ArrayList<File>(Arrays.asList(fileArray)); Collections.sort(fileList, new Comparator<File>() { @Override public int compare(File file1, File file2) { String name1 = file1.getName(); String name2 = file2.getName(); return Long.valueOf(name1).compareTo(Long.valueOf(name2)); } }); if (fileList.isEmpty()) { // There are no persisted subsystems, including root. SubsystemResource resource; try { resource = new SubsystemResource(file); } catch (SubsystemException e) { throw e; } catch (Exception e) { throw new SubsystemException(e); } Coordination coordination = Utils.createCoordination(); try { root = new BasicSubsystem(resource); // TODO This initialization is a bit brittle. The root subsystem // must be gotten before anything else will be able to use the // graph. At the very least, throw IllegalStateException where // appropriate. graph = new SubsystemGraph(root); ResourceInstaller.newInstance(coordination, root, root).install(); populateRootSubsystem(root, coordination); } catch (Exception e) { coordination.fail(e); } finally { coordination.end(); } } else { // There are persisted subsystems. Coordination coordination = Utils.createCoordination(); Collection<BasicSubsystem> subsystems = new ArrayList<BasicSubsystem>(fileList.size()); try { for (File f : fileList) { BasicSubsystem s = new BasicSubsystem(f); if (State.UNINSTALLED.equals(s.getState())) { // left over cache, delete this IOUtils.deleteRecursive(f); } else { subsystems.add(s); addSubsystem(s); } } root = getSubsystemById(0); SubsystemIdentifier.setLastId( Long.parseLong( root.getDeploymentManifest().getHeaders().get( DeploymentManifest.ARIESSUBSYSTEM_LASTID).getValue())); graph = new SubsystemGraph(root); ResourceInstaller.newInstance(coordination, root, root).install(); populateRootSubsystem(root, coordination); } catch (Exception e) { coordination.fail(e); } finally { coordination.end(); } } } return root; } private void populateRootSubsystem(BasicSubsystem root, Coordination coordination) throws Exception { // TODO Begin proof of concept. // This is a proof of concept for initializing the relationships between the root subsystem and bundles // that already existed in its region. Not sure this will be the final resting place. Plus, there are issues // since this does not take into account the possibility of already existing bundles going away or new bundles // being installed out of band while this initialization is taking place. Need a bundle event hook for that. BundleContext context = Activator.getInstance().getBundleContext().getBundle(org.osgi.framework.Constants.SYSTEM_BUNDLE_LOCATION).getBundleContext(); for (Bundle bundle : context.getBundles()) { BundleRevision revision = bundle.adapt(BundleRevision.class); if (revision == null) // The bundle has been uninstalled. Do not process. continue; if (!resourceReferences.getSubsystems(revision).isEmpty()) continue; ResourceInstaller.newInstance(coordination, revision, root).install(); } // TODO End proof of concept. } public BasicSubsystem getSubsystemById(long id) { synchronized (idToSubsystem) { return idToSubsystem.get(id); } } public BasicSubsystem getSubsystemByLocation(String location) { synchronized (locationToSubsystem) { return locationToSubsystem.get(location); } } public Collection<BasicSubsystem> getSubsystems() { return new ArrayList<BasicSubsystem>(idToSubsystem.values()); } // TODO Not very pretty. A quick fix. public Object[] getSubsystemsByBundle(Bundle bundle) { BundleRevision revision = null; ArrayList<BasicSubsystem> result = new ArrayList<BasicSubsystem>(); synchronized (subsystemToConstituents) { for (BasicSubsystem subsystem : subsystemToConstituents.keySet()) { for (Resource constituent : getConstituents(subsystem)) { if (constituent instanceof BundleConstituent && ((BundleConstituent)constituent).getBundle() == bundle) { result.add(subsystem); revision = ((BundleConstituent)constituent).getRevision(); } } } } result.trimToSize(); if (revision == null) return null; return new Object[]{revision, result}; } public Collection<BasicSubsystem> getSubsystemsByConstituent(Resource constituent) { ArrayList<BasicSubsystem> result = new ArrayList<BasicSubsystem>(); synchronized (subsystemToConstituents) { for (BasicSubsystem subsystem : subsystemToConstituents.keySet()) if (getConstituents(subsystem).contains(constituent)) result.add(subsystem); } result.trimToSize(); return result; } public Collection<BasicSubsystem> getSubsystemsReferencing(Resource resource) { return resourceReferences.getSubsystems(resource); } public void removeChild(BasicSubsystem child) { graph.remove(child); } public void removeChild(BasicSubsystem parent, BasicSubsystem child) { graph.remove(parent, child); } public void removeConstituent(BasicSubsystem subsystem, Resource constituent) { synchronized (subsystemToConstituents) { Set<Resource> constituents = subsystemToConstituents.get(subsystem); if (constituents != null) { constituents.remove(constituent); if (constituents.isEmpty()) subsystemToConstituents.remove(subsystem); } } subsystem.removedContent(constituent); } public void removeReference(BasicSubsystem subsystem, Resource resource) { resourceReferences.removeReference(subsystem, resource); } public void removeSubsystem(BasicSubsystem subsystem) { synchronized (idToSubsystem) { synchronized (locationToSubsystem) { removeLocationToSubsystem(subsystem); removeIdToSubsystem(subsystem); } } } private void addIdToSubsystem(BasicSubsystem subsystem) { long id = subsystem.getSubsystemId(); idToSubsystem.put(id, subsystem); } private void addLocationToSubsystem(BasicSubsystem subsystem) { String location = subsystem.getLocation(); locationToSubsystem.put(location, subsystem); } private void removeIdToSubsystem(BasicSubsystem subsystem) { long id = subsystem.getSubsystemId(); idToSubsystem.remove(id); } private void removeLocationToSubsystem(BasicSubsystem subsystem) { String location = subsystem.getLocation(); locationToSubsystem.remove(location); } }