blob: b4f4bb3e1fba63cd2bdcdeb01a6346e85b75c0d6 [file] [log] [blame]
/*
* 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 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.builder.ArtifactProvider;
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.feature.spi.context.ExtensionHandler;
import org.apache.sling.feature.spi.context.ExtensionHandlerContext;
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;
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.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
/**
* This task installs a feature model resources.
*/
public class InstallFeatureModelTask extends AbstractFeatureModelTask {
private final InstallContext installContext;
private final List<ExtensionHandler> extensionHandlers;
public InstallFeatureModelTask(final TaskResourceGroup group,
final InstallContext installContext, final BundleContext bundleContext,
final List<ExtensionHandler> extensionHandlers) {
super(group, bundleContext);
this.installContext = installContext;
this.extensionHandlers = extensionHandlers;
}
@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.toMvnPath().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));
}
// 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;
}
}
ExtensionHandlerContext context = new ContextImpl(result);
for (Extension ext : feature.getExtensions()) {
boolean handlerFound = false;
for (ExtensionHandler eh : extensionHandlers) {
try {
handlerFound |= eh.handle(context, ext, feature);
} catch (Exception e) {
logger.error("Exception while processing extension {} with handler {}", ext, eh, e);
}
}
if (!handlerFound) {
if (ExtensionType.ARTIFACTS == ext.getType()) {
// Unhandled ARTIFACTS extensions get stored
for (final Artifact artifact : ext.getArtifacts()) {
addArtifact(artifact, result);
}
} else {
// should this be an error?
logger.warn("No extension handler found for mandartory extension " + ext);
}
}
}
// bundles
for (final Artifact bundle : feature.getBundles()) {
if (!addArtifact(bundle, result)) {
return null;
}
}
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);
}
private ArtifactProvider getLocalArtifactProvider() {
// TODO share with addArtifact()
return new ArtifactProvider() {
@Override
public URL provide(ArtifactId id) {
File artifactFile = (installContext.storageDirectory == null ? null
: getArtifactFile(installContext.storageDirectory, id));
ArtifactHandler handler;
if (artifactFile == null || !artifactFile.exists()) {
try {
handler = installContext.artifactManager.getArtifactHandler(id.toMvnUrl());
} catch (final IOException ignore) {
return null;
}
} else {
try {
handler = new ArtifactHandler(artifactFile);
} catch (final MalformedURLException e) {
return null;
}
}
if (handler == null) {
return null;
}
return handler.getLocalURL();
}
};
}
private class ContextImpl implements ExtensionHandlerContext {
private final List<InstallableResource> results;
public ContextImpl(List<InstallableResource> results) {
this.results = results;
}
@Override
public void addBundle(ArtifactId id, URL file, Integer startLevel) {
// TODO Auto-generated method stub
}
@Override
public void addInstallableArtifact(ArtifactId id, URL url, Map<String,Object> props) {
try {
Dictionary <String,Object> dict = new Hashtable<>();
props.entrySet().stream()
.filter(e -> e.getValue() != null)
.forEach(e -> dict.put(e.getKey(), e.getValue()));
InputStream is = url.openStream();
results.add(new InstallableResource("/".concat(id.toMvnName()), is, dict, null /* TODO digest? */,
InstallableResource.TYPE_FILE, null));
} catch (IOException e) {
logger.warn("Unable to read artifact " + id + " from url " + url, e);
}
}
@Override
public void addConfiguration(String pid, String factoryPid, Dictionary<String, Object> properties) {
// TODO handler factoryPid, is this ok?
String cfgPid = pid;
if (factoryPid != null) {
cfgPid = factoryPid;
}
results.add(new InstallableResource("/".concat(cfgPid).concat(".config"), null,
properties, null, InstallableResource.TYPE_CONFIG, null));
}
@Override
public ArtifactProvider getArtifactProvider() {
return getLocalArtifactProvider();
}
}
}