| /* |
| * 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 javax.jcr.RepositoryException; |
| import javax.jcr.Session; |
| |
| 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.io.archive.ArchiveReader; |
| import org.apache.sling.feature.io.artifacts.ArtifactHandler; |
| import org.apache.sling.feature.io.artifacts.ArtifactManager; |
| 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.apache.sling.jcr.api.SlingRepository; |
| import org.apache.sling.jcr.repoinit.JcrRepoInitOpsProcessor; |
| import org.apache.sling.repoinit.parser.RepoInitParser; |
| import org.apache.sling.repoinit.parser.RepoInitParsingException; |
| import org.apache.sling.repoinit.parser.operations.Operation; |
| import org.osgi.framework.BundleContext; |
| |
| /** |
| * This task installs a feature model resources. |
| */ |
| public class InstallFeatureModelTask extends AbstractFeatureModelTask { |
| |
| private final SlingRepository repository; |
| |
| private final JcrRepoInitOpsProcessor repoInitProcessor; |
| |
| private final RepoInitParser repoInitParser; |
| |
| private final ArtifactManager artifactManager; |
| |
| public InstallFeatureModelTask(final TaskResourceGroup group, |
| final SlingRepository repository, |
| final JcrRepoInitOpsProcessor repoInitProcessor, |
| final RepoInitParser repoInitParser, |
| final BundleContext bundleContext, final ArtifactManager artifactManager) { |
| super(group, bundleContext); |
| this.repository = repository; |
| this.repoInitProcessor = repoInitProcessor; |
| this.repoInitParser = repoInitParser; |
| this.artifactManager = artifactManager; |
| } |
| |
| @SuppressWarnings("deprecation") |
| @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); |
| } else { |
| final String path = (String) resource.getAttribute(FeatureModelInstallerPlugin.ATTR_BASE_PATH); |
| final File baseDir = (path == null ? null : new File(path)); |
| |
| boolean success = false; |
| try { |
| final Result result = this.transform(featureJson, resource, baseDir); |
| if ( result == null ) { |
| ctx.log("Unable to install feature model resource {} : unable to create resources", resource); |
| this.getResourceGroup().setFinishState(ResourceState.IGNORED); |
| } else { |
| // repo init first |
| if ( result.repoinit != null ) { |
| List<Operation> ops = null; |
| try ( final Reader r = new StringReader(result.repoinit) ) { |
| ops = this.repoInitParser.parse(r); |
| } catch (final IOException | RepoInitParsingException e) { |
| logger.error("Unable to parse repoinit text.", e); |
| ctx.log("Unable to install feature model resource {} : unable parse repoinit text.", |
| resource); |
| this.getResourceGroup().setFinishState(ResourceState.IGNORED); |
| return; |
| } |
| |
| // login admin is required for repo init |
| Session session = null; |
| try { |
| session = this.repository.loginAdministrative(null); |
| this.repoInitProcessor.apply(session, ops); |
| session.save(); |
| } catch ( final RepositoryException re) { |
| logger.error("Unable to process repoinit text.", re); |
| ctx.log("Unable to install feature model resource {} : unable to process repoinit text.", |
| resource); |
| this.getResourceGroup().setFinishState(ResourceState.IGNORED); |
| return; |
| |
| } finally { |
| if ( session != null ) { |
| session.logout(); |
| } |
| } |
| } |
| if ( !result.resources.isEmpty() ) { |
| final OsgiInstaller installer = this.getService(OsgiInstaller.class); |
| if ( installer != null ) { |
| installer.registerResources( |
| "model-" + resource.getAttribute(FeatureModelInstallerPlugin.ATTR_ID), |
| result.resources.toArray(new InstallableResource[result.resources.size()])); |
| } else { |
| ctx.log("Unable to install feature model resource {} : unable to get OSGi installer", |
| resource); |
| this.getResourceGroup().setFinishState(ResourceState.IGNORED); |
| return; |
| } |
| } |
| this.getResourceGroup().setFinishState(ResourceState.INSTALLED); |
| success = true; |
| } |
| } finally { |
| if ( !success && baseDir != null ) { |
| this.deleteDirectory(baseDir); |
| } |
| } |
| if ( success ) { |
| ctx.log("Installed {}", resource.getEntityId()); |
| } |
| } |
| } finally { |
| this.cleanup(); |
| } |
| } |
| |
| public static final class Result { |
| public final List<InstallableResource> resources = new ArrayList<>(); |
| public String repoinit; |
| } |
| |
| private File getArtifactFile(final File baseDir, final ArtifactId id) { |
| return new File(baseDir, id.toMvnId().replace('/', File.separatorChar)); |
| } |
| |
| private Result transform(final String featureJson, |
| final TaskResource rsrc, |
| final File baseDir) { |
| 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; |
| } |
| |
| if ( baseDir != null ) { |
| // extract artifacts |
| 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(baseDir, 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; |
| } |
| } |
| |
| |
| final Result result = new Result(); |
| for (final Artifact bundle : feature.getBundles()) { |
| if (!addArtifact(baseDir, bundle, result)) { |
| return null; |
| } |
| } |
| 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(baseDir, artifact, result); |
| } |
| } |
| |
| for (final Configuration cfg : feature.getConfigurations()) { |
| result.resources.add(new InstallableResource("/".concat(cfg.getPid()).concat(".config"), null, |
| cfg.getConfigurationProperties(), null, InstallableResource.TYPE_CONFIG, null)); |
| } |
| |
| final Extension repoInit = feature.getExtensions().getByName(Extension.EXTENSION_NAME_REPOINIT); |
| if (repoInit != null && repoInit.getType() == ExtensionType.TEXT) { |
| result.repoinit = repoInit.getText(); |
| } |
| return result; |
| } |
| |
| private boolean addArtifact(final File baseDir, final Artifact artifact, |
| final Result result) { |
| File artifactFile = (baseDir == null ? null : getArtifactFile(baseDir, artifact.getId())); |
| ArtifactHandler handler; |
| if (artifactFile == null || !artifactFile.exists()) { |
| try { |
| handler = this.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.resources.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); |
| } |
| } |