/******************************************************************************* * Copyright (c) 2008, 2010 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: * VMware Inc. - initial contribution *******************************************************************************/ package org.eclipse.virgo.kernel.install.artifact.internal; import static java.nio.charset.StandardCharsets.UTF_8; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.jar.JarFile; import org.eclipse.virgo.kernel.artifact.fs.ArtifactFS; import org.eclipse.virgo.kernel.artifact.fs.ArtifactFSEntry; import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository; import org.eclipse.virgo.medic.eventlog.EventLogger; import org.eclipse.virgo.nano.deployer.api.core.DeploymentException; import org.eclipse.virgo.util.io.IOUtils; import org.eclipse.virgo.util.osgi.manifest.BundleManifest; import org.eclipse.virgo.util.osgi.manifest.BundleManifestFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.xml.XmlValidationModeDetector; /** * Generates the service model in the {@link StandardScopeServiceRepository} for a bundle in a given scope. * <p/> * * <strong>Concurrent Semantics</strong><br /> * * Not thread safe. * */ final class ServiceScoper { private static final String SPRING_CONFIG_DIR = "META-INF/spring/"; private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final XmlValidationModeDetector xmlValidationModeDetector = new XmlValidationModeDetector(); private final String scopeName; private final ScopeServiceRepository repository; private final EventLogger eventLogger; /** * Creates a new <code>ServiceScoper</code> for the supplied scope name. * * @param scopeName supplied * @param scopeServiceRepository the {@link StandardScopeServiceRepository}. * @param eventLogger logger for events */ public ServiceScoper(String scopeName, ScopeServiceRepository scopeServiceRepository, EventLogger eventLogger) { this.scopeName = scopeName; this.repository = scopeServiceRepository; this.eventLogger = eventLogger; } /** * Scopes the application's services. * * @param modules set of stored artifacts to search for configurations * @throws DeploymentException if configuration files or manifests are not well-formed */ public void scope(Set<ArtifactFS> modules) throws DeploymentException { Map<ArtifactFS, List<ArtifactFSEntry>> configFiles = new HashMap<ArtifactFS, List<ArtifactFSEntry>>(); for (ArtifactFS moduleData : modules) { configFiles.put(moduleData, findConfigFiles(moduleData)); } doScope(configFiles); } private List<ArtifactFSEntry> findConfigFiles(ArtifactFS bundleData) throws DeploymentException { List<ArtifactFSEntry> configFiles = new ArrayList<ArtifactFSEntry>(); ArtifactFSEntry entry = bundleData.getEntry(SPRING_CONFIG_DIR); if (entry.exists()) { try { configFiles.addAll(findConfigFiles(bundleData, entry)); } catch (IOException e) { throw new DeploymentException("Unable to read Spring config files.", e); } } return configFiles; } private List<ArtifactFSEntry> findConfigFiles(ArtifactFS bundleData, ArtifactFSEntry entry) throws IOException { ArtifactFSEntry[] children = entry.getChildren(); List<ArtifactFSEntry> configFiles = new ArrayList<ArtifactFSEntry>(); for (ArtifactFSEntry e : children) { if (e.isDirectory()) { configFiles.addAll(findConfigFiles(bundleData, e)); } else if (e.getPath().endsWith(".xml")) { try { InputStream is = e.getInputStream(); int validationMode; try { validationMode = xmlValidationModeDetector.detectValidationMode(is); } finally { IOUtils.closeQuietly(is); } if (validationMode != XmlValidationModeDetector.VALIDATION_DTD) { configFiles.add(e); } else { logger.debug("Skipping entry '{}' as it uses a DTD.", e); } } catch (IOException ioe) { logger.debug("Unexpected error detecting validation mode of entry '{}'", ioe, e); configFiles.add(e); } } } return configFiles; } /** * Re-scope the service exports and service references of the given {@link ArtifactFS}. * * @param bundleData the {@link ArtifactFS} to be re-scoped. * @throws DeploymentException */ public void rescope(ArtifactFS bundleData) throws DeploymentException { Map<ArtifactFS, List<ArtifactFSEntry>> configFiles = new HashMap<ArtifactFS, List<ArtifactFSEntry>>(); configFiles.put(bundleData, findConfigFiles(bundleData)); doScope(configFiles); } /** * Updates the {@link StandardScopeServiceRepository} with the service information from the given config files. * * @param scopeName the name of the scope. * @param configFiles the config files to scope. * @throws DeploymentException */ private void doScope(Map<ArtifactFS, List<ArtifactFSEntry>> configFiles) throws DeploymentException { SpringConfigServiceModelScanner scanner = new SpringConfigServiceModelScanner(this.scopeName, this.repository, this.eventLogger); Map<ArtifactFS, BundleManifest> manifests = loadBundleManifests(configFiles.keySet()); for (Entry<ArtifactFS, List<ArtifactFSEntry>> entry : configFiles.entrySet()) { BundleManifest bundleManifest = manifests.get(entry.getKey()); for (ArtifactFSEntry configFile : entry.getValue()) { InputStream is = configFile.getInputStream(); try { scanner.scanConfigFile(bundleManifest.getBundleSymbolicName().getSymbolicName(), bundleManifest.getBundleVersion(), configFile.getPath(), is); } finally { IOUtils.closeQuietly(is); } } } } private Map<ArtifactFS, BundleManifest> loadBundleManifests(Collection<ArtifactFS> modules) throws DeploymentException { Map<ArtifactFS, BundleManifest> result = new HashMap<ArtifactFS, BundleManifest>(); for (ArtifactFS module : modules) { if (!result.containsKey(module)) { BundleManifest manifest = loadManifest(module); result.put(module, manifest); } } return result; } private BundleManifest loadManifest(ArtifactFS compositeArtifactFS) throws DeploymentException { ArtifactFSEntry entry = compositeArtifactFS.getEntry(JarFile.MANIFEST_NAME); Reader reader = null; try { reader = new InputStreamReader(entry.getInputStream(), UTF_8); return BundleManifestFactory.createBundleManifest(reader); } catch (IOException ex) { throw new DeploymentException("Error reading MANIFEST.MF from '" + compositeArtifactFS + "'", ex); } finally { IOUtils.closeQuietly(reader); } } }