| /* |
| * 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.installer.factory.model.impl; |
| |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.net.MalformedURLException; |
| import java.net.URLConnection; |
| import java.util.ArrayList; |
| import java.util.Dictionary; |
| import java.util.Hashtable; |
| import java.util.List; |
| |
| import org.apache.sling.feature.Artifact; |
| import org.apache.sling.feature.ArtifactId; |
| import org.apache.sling.feature.Configuration; |
| import org.apache.sling.feature.Extension; |
| import org.apache.sling.feature.ExtensionType; |
| import org.apache.sling.feature.Feature; |
| import org.apache.sling.feature.extension.apiregions.api.ApiRegions; |
| import org.apache.sling.feature.extension.apiregions.launcher.LauncherProperties; |
| import org.apache.sling.feature.io.archive.ArchiveReader; |
| import org.apache.sling.feature.io.artifacts.ArtifactHandler; |
| import org.apache.sling.feature.io.json.FeatureJSONReader; |
| import org.apache.sling.installer.api.InstallableResource; |
| import org.apache.sling.installer.api.OsgiInstaller; |
| import org.apache.sling.installer.api.tasks.InstallationContext; |
| import org.apache.sling.installer.api.tasks.ResourceState; |
| import org.apache.sling.installer.api.tasks.TaskResource; |
| import org.apache.sling.installer.api.tasks.TaskResourceGroup; |
| import org.osgi.framework.BundleContext; |
| |
| /** |
| * This task installs a feature model resources. |
| */ |
| public class InstallFeatureModelTask extends AbstractFeatureModelTask { |
| |
| private static final String PROP_idbsnver = "mapping.bundleid.bsnver"; |
| private static final String PROP_bundleFeatures = "mapping.bundleid.features"; |
| private static final String PROP_featureRegions = "mapping.featureid.regions"; |
| private static final String PROP_regionPackage = "mapping.region.packages"; |
| |
| private static final String REGION_FACTORY_PID = "org.apache.sling.feature.apiregions.factory~"; |
| private static final String REPOINIT_FACTORY_PID = "org.apache.sling.jcr.repoinit.RepositoryInitializer~"; |
| |
| private final InstallContext installContext; |
| |
| public InstallFeatureModelTask(final TaskResourceGroup group, |
| final InstallContext installContext, final BundleContext bundleContext) { |
| super(group, bundleContext); |
| this.installContext = installContext; |
| } |
| |
| @Override |
| public void execute(final InstallationContext ctx) { |
| try { |
| final TaskResource resource = this.getResource(); |
| ctx.log("Installing {}", resource.getEntityId()); |
| final String featureJson = (String) resource.getAttribute(FeatureModelInstallerPlugin.ATTR_MODEL); |
| if (featureJson == null) { |
| ctx.log("Unable to install feature model resource {} : no model found", resource); |
| this.getResourceGroup().setFinishState(ResourceState.IGNORED, null, "No model found"); |
| } else { |
| boolean success = false; |
| final List<InstallableResource> result = this.transform(featureJson, resource); |
| if (result == null) { |
| ctx.log("Unable to install feature model resource {} : unable to create resources", resource); |
| this.getResourceGroup().setFinishState(ResourceState.IGNORED, null, "Unable to create resources"); |
| } else { |
| if (!result.isEmpty()) { |
| final OsgiInstaller installer = this.getService(OsgiInstaller.class); |
| if (installer != null) { |
| installer.registerResources( |
| getScheme(resource), |
| result.toArray(new InstallableResource[result.size()])); |
| } else { |
| ctx.log("Unable to install feature model resource {} : unable to get OSGi installer", |
| resource); |
| this.getResourceGroup().setFinishState(ResourceState.IGNORED, null, "Unable to get OSGi installer"); |
| return; |
| } |
| } |
| this.getResourceGroup().setFinishState(ResourceState.INSTALLED); |
| success = true; |
| } |
| if ( success ) { |
| ctx.log("Installed {}", resource.getEntityId()); |
| } |
| } |
| } finally { |
| this.cleanup(); |
| } |
| } |
| |
| private File getArtifactFile(final File baseDir, final ArtifactId id) { |
| return new File(baseDir, id.toMvnId().replace('/', File.separatorChar)); |
| } |
| |
| private List<InstallableResource> transform(final String featureJson, |
| final TaskResource rsrc) { |
| Feature feature = null; |
| try (final Reader reader = new StringReader(featureJson)) { |
| feature = FeatureJSONReader.read(reader, null); |
| } catch ( final IOException ioe) { |
| logger.warn("Unable to read feature model file", ioe); |
| } |
| if (feature == null) { |
| return null; |
| } |
| |
| final List<InstallableResource> result = new ArrayList<>(); |
| // configurations |
| for (final Configuration cfg : feature.getConfigurations()) { |
| result.add(new InstallableResource("/".concat(cfg.getPid()).concat(".config"), null, |
| cfg.getConfigurationProperties(), null, InstallableResource.TYPE_CONFIG, null)); |
| } |
| |
| // repoinit |
| final Extension repoInit = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); |
| if (repoInit != null && repoInit.getType() == ExtensionType.TEXT) { |
| final String configPid = REPOINIT_FACTORY_PID.concat(feature.getId().toMvnName().replace('-', '_')); |
| final Dictionary<String, Object> props = new Hashtable<>(); |
| props.put("scripts", repoInit.getText()); |
| |
| result.add(new InstallableResource("/".concat(configPid).concat(".config"), null, |
| props, null, InstallableResource.TYPE_CONFIG, null)); |
| } |
| |
| // extract artifacts |
| if (this.installContext.storageDirectory != null) { |
| final byte[] buffer = new byte[1024*1024*256]; |
| |
| try ( final InputStream is = rsrc.getInputStream() ) { |
| ArchiveReader.read(is, new ArchiveReader.ArtifactConsumer() { |
| |
| @Override |
| public void consume(final ArtifactId id, final InputStream is) throws IOException { |
| final File artifactFile = getArtifactFile(installContext.storageDirectory, id); |
| if (!artifactFile.exists()) { |
| artifactFile.getParentFile().mkdirs(); |
| try (final OutputStream os = new FileOutputStream(artifactFile)) { |
| int l = 0; |
| while ((l = is.read(buffer)) > 0) { |
| os.write(buffer, 0, l); |
| } |
| } |
| } |
| } |
| }); |
| } catch ( final IOException ioe) { |
| logger.warn("Unable to extract artifacts from feature model " + feature.getId().toMvnId(), ioe); |
| return null; |
| } |
| } |
| |
| // api regions |
| final Extension regionExt = feature.getExtensions().getByName(ApiRegions.EXTENSION_NAME); |
| if ( regionExt != null ) { |
| try { |
| final ApiRegions regions = ApiRegions.parse(regionExt.getJSONStructure().asJsonArray()); |
| |
| final String configPid = REGION_FACTORY_PID.concat(feature.getId().toMvnName().replace('-', '_')); |
| final Dictionary<String, Object> props = new Hashtable<>(); |
| props.put(PROP_idbsnver, LauncherProperties.getBundleIDtoBSNandVersionMap(feature, this.installContext.artifactManager)); |
| props.put(PROP_bundleFeatures, LauncherProperties.getBundleIDtoFeaturesMap(feature)); |
| props.put(PROP_featureRegions, LauncherProperties.getFeatureIDtoRegionsMap(regions)); |
| props.put(PROP_regionPackage, LauncherProperties.getRegionNametoPackagesMap(regions)); |
| |
| result.add(new InstallableResource("/".concat(configPid).concat(".config"), null, |
| props, null, InstallableResource.TYPE_CONFIG, null)); |
| } catch (final IOException ioe) { |
| logger.warn("Unable to parse region information " + feature.getId().toMvnId(), ioe); |
| return null; |
| } |
| } |
| |
| // bundles |
| for (final Artifact bundle : feature.getBundles()) { |
| if (!addArtifact(bundle, result)) { |
| return null; |
| } |
| } |
| |
| // content packages |
| final Extension ext = feature.getExtensions().getByName(Extension.EXTENSION_NAME_CONTENT_PACKAGES); |
| if (ext != null && ext.getType() == ExtensionType.ARTIFACTS) { |
| for (final Artifact artifact : ext.getArtifacts()) { |
| addArtifact(artifact, result); |
| } |
| } |
| |
| return result; |
| } |
| |
| private boolean addArtifact(final Artifact artifact, |
| final List<InstallableResource> result) { |
| File artifactFile = (this.installContext.storageDirectory == null ? null |
| : getArtifactFile(this.installContext.storageDirectory, artifact.getId())); |
| ArtifactHandler handler; |
| if (artifactFile == null || !artifactFile.exists()) { |
| try { |
| handler = this.installContext.artifactManager.getArtifactHandler(artifact.getId().toMvnUrl()); |
| } catch (final IOException ignore) { |
| return false; |
| } |
| } else { |
| try { |
| handler = new ArtifactHandler(artifactFile); |
| } catch (final MalformedURLException e) { |
| return false; |
| } |
| } |
| if (handler == null) { |
| return false; |
| } |
| try { |
| final URLConnection connection = handler.getLocalURL().openConnection(); |
| connection.connect(); |
| final InputStream is = connection.getInputStream(); |
| final long lastModified = connection.getLastModified(); |
| final String digest = lastModified == 0 ? null : String.valueOf(lastModified); |
| // handle start order |
| final Dictionary<String, Object> dict = new Hashtable<String, Object>(); |
| if (artifact.getStartOrder() > 0) { |
| dict.put(InstallableResource.BUNDLE_START_LEVEL, artifact.getStartOrder()); |
| } |
| dict.put(InstallableResource.RESOURCE_URI_HINT, handler.getLocalURL().toString()); |
| |
| result.add(new InstallableResource("/".concat(artifact.getId().toMvnName()), is, dict, digest, |
| InstallableResource.TYPE_FILE, null)); |
| } catch (final IOException ioe) { |
| logger.warn("Unable to read artifact " + handler.getLocalURL(), ioe); |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public String getSortKey() { |
| return "30-" + getResource().getAttribute(FeatureModelInstallerPlugin.ATTR_ID); |
| } |
| } |