/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to You 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.sling.launchpad.installer.impl; import java.io.IOException; import java.io.InputStream; import java.net.URISyntaxException; import java.net.URL; import java.util.Collection; import java.util.Dictionary; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.Set; import org.apache.sling.installer.api.InstallableResource; import org.apache.sling.installer.api.OsgiInstaller; import org.apache.sling.launchpad.api.LaunchpadContentProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class scans the launchpad resources folder and provides the artifacts * to the OSGi installer. */ public class LaunchpadConfigInstaller { /** Logger */ private final Logger logger = LoggerFactory.getLogger(this.getClass().getName()); /** * Resources supplied under this path by * LaunchpadContentProvider are considered for installation */ private static final String ROOT_PATH = "resources"; /** * Resources supplied under this path by * LaunchpadContentProvider are considered for installation * as configurations */ private static final String CONFIG_NAME = "config"; /** * Resources supplied under this path by * LaunchpadContentProvider are considered for installation * as files */ private static final String INSTALL_NAME = "install"; private static final String INSTALL_PREFIX = "install."; /** Artifact priority. */ private static final Integer PRIORITY = new Integer(50); private static final int PRIORITY_BOOST = 5; /** * Check the path for installable artifacts. */ private boolean checkPath(final String rootPath, final String resourceType, Integer prio) { int count = 0; final Iterator<String> configPaths = resourceProvider.getChildren(rootPath); if ( configPaths != null ) { final int hintPos = rootPath.lastIndexOf('/'); final String hint = rootPath.substring(hintPos + 1); while (configPaths.hasNext()) { String path = configPaths.next(); if ( path.endsWith("/") ) { path = path.substring(0, path.length() - 1); } if ( !checkPath(path, resourceType, prio) ) { count++; final URL url = resourceProvider.getResource(path); if(url == null){ logger.debug("Launchpad ignoring path '{}' due to null URL", path); continue; } Dictionary<String, Object> dict = null; if ( InstallableResource.TYPE_FILE.equals(resourceType) ) { dict = new Hashtable<String, Object>(); if ( !hint.startsWith(INSTALL_NAME) ) { dict.put(InstallableResource.INSTALLATION_HINT, hint); } try { dict.put(InstallableResource.RESOURCE_URI_HINT, url.toURI().toString()); } catch (final URISyntaxException e) { // we just ignore this } } else if ( !hint.equals(CONFIG_NAME) ) { final int activeModes = isActive(hint); if ( activeModes == 0 ) { logger.debug("Launchpad ignoring {} : {} due to unactivated run mode: {}", new Object[] {resourceType, path, hint}); continue; } prio = PRIORITY + PRIORITY_BOOST * activeModes; } long lastModified = -1; try { lastModified = url.openConnection().getLastModified(); } catch (final IOException e) { // we ignore this } logger.debug("Launchpad {} will be registered: {}", resourceType, path); final String digest = (lastModified > 0 ? String.valueOf(lastModified) : null); final InputStream stream = resourceProvider.getResourceAsStream(path); installables.add(new InstallableResource(path, stream, dict, digest, resourceType, prio)); } } } return count > 0; } private int isActive(final String runModesString) { final String[] runModes = runModesString.split("\\."); boolean active = true; for(final String mode : runModes) { if ( !activeRunModes.contains(mode) ) { active = false; break; } } return active ? runModes.length : 0; } /** * Install artifacts */ public static void install(final OsgiInstaller installer, final LaunchpadContentProvider resourceProvider, final Set<String> activeRunModes) { new LaunchpadConfigInstaller(resourceProvider, activeRunModes).install(installer); } private final LaunchpadContentProvider resourceProvider; private final Set<String> activeRunModes; private final Collection<InstallableResource> installables = new HashSet<InstallableResource>(); private LaunchpadConfigInstaller(final LaunchpadContentProvider resourceProvider, final Set<String> activeRunModes) { this.resourceProvider = resourceProvider; this.activeRunModes = activeRunModes; } private void install(final OsgiInstaller installer) { logger.info("Activating launchpad config installer, configuration path={}/{}, install path={}/{}", new Object[] {ROOT_PATH, CONFIG_NAME, ROOT_PATH, INSTALL_NAME}); final Iterator<String> configPaths = resourceProvider.getChildren(ROOT_PATH); if ( configPaths != null ) { while ( configPaths.hasNext() ) { String path = configPaths.next(); logger.debug("Found launchpad resource {}", path); if ( path.endsWith("/") ) { path = path.substring(0, path.length() - 1); } final int namePos = path.lastIndexOf('/'); String name = path.substring(namePos + 1); if ( name.equals(CONFIG_NAME) ) { // configurations checkPath(path, InstallableResource.TYPE_PROPERTIES, PRIORITY); } else if ( name.equals(INSTALL_NAME) ) { // files checkPath(path, InstallableResource.TYPE_FILE, PRIORITY); } else if ( name.startsWith(INSTALL_PREFIX) ) { final int activeModes = isActive(name.substring(INSTALL_PREFIX.length())); if ( activeModes > 0 ) { // files final int prio = PRIORITY + PRIORITY_BOOST * activeModes; checkPath(path, InstallableResource.TYPE_FILE, prio); } else { logger.debug("Ignoring path {} due to unactivated run mode: {}", path, name.substring(INSTALL_PREFIX.length())); } } else { logger.debug("Ignoring path {} - not an installation path", path); } } } else { logger.warn("Run mode dependent installation not supported by launchpad content provider {}", resourceProvider); // revert to old behavior checkPath(ROOT_PATH + '/' + CONFIG_NAME, InstallableResource.TYPE_PROPERTIES, PRIORITY); checkPath(ROOT_PATH + '/' + INSTALL_NAME, InstallableResource.TYPE_FILE, PRIORITY); } final InstallableResource [] toInstall = installables.toArray(new InstallableResource []{}); installer.registerResources("launchpad", (toInstall)); logger.info("{} resources registered with OsgiInstaller", toInstall.length); } }