blob: c9d9ad3b7d32f27b75295ce850db70e5c76f212c [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.distribution.packaging.impl;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.commons.io.IOUtils;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.distribution.DistributionRequest;
import org.apache.sling.distribution.DistributionRequestType;
import org.apache.sling.distribution.common.DistributionException;
import org.apache.sling.distribution.packaging.DistributionPackage;
import org.apache.sling.distribution.packaging.DistributionPackageBuilder;
import org.apache.sling.distribution.packaging.DistributionPackageInfo;
import org.apache.sling.distribution.serialization.impl.vlt.VltUtils;
import org.apache.sling.distribution.util.DistributionJcrUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* base abstract implementation of a JCR based {@link DistributionPackageBuilder}
*/
public abstract class AbstractDistributionPackageBuilder implements DistributionPackageBuilder {
private final Logger log = LoggerFactory.getLogger(getClass());
private final String type;
AbstractDistributionPackageBuilder(String type) {
this.type = type;
}
public String getType() {
return type;
}
@Nonnull
public DistributionPackage createPackage(@Nonnull ResourceResolver resourceResolver, @Nonnull DistributionRequest request)
throws DistributionException {
DistributionPackage distributionPackage;
if (!VltUtils.isSupported(request)) {
throw new DistributionException("unsupported request " + request);
}
if (DistributionRequestType.ADD.equals(request.getRequestType())) {
distributionPackage = createPackageForAdd(resourceResolver, request);
} else if (DistributionRequestType.DELETE.equals(request.getRequestType())) {
distributionPackage = new SimpleDistributionPackage(request, type);
} else if (DistributionRequestType.PULL.equals(request.getRequestType())) {
distributionPackage = new SimpleDistributionPackage(request, type);
} else if (DistributionRequestType.TEST.equals(request.getRequestType())) {
distributionPackage = new SimpleDistributionPackage(request, type);
} else {
throw new DistributionException("unknown action type " + request.getRequestType());
}
DistributionPackageUtils.fillInfo(distributionPackage.getInfo(), request);
return distributionPackage;
}
@Nonnull
public DistributionPackage readPackage(@Nonnull ResourceResolver resourceResolver, @Nonnull InputStream stream) throws DistributionException {
if (!stream.markSupported()) {
stream = new BufferedInputStream(stream);
}
Map<String, Object> headerInfo = new HashMap<String, Object>();
DistributionPackageUtils.readInfo(stream, headerInfo);
try {
stream.reset();
} catch (IOException e) {
// do nothing
}
DistributionPackage distributionPackage = SimpleDistributionPackage.fromStream(stream, type);
try {
stream.reset();
} catch (IOException e) {
// do nothing
}
// not a simple package
if (distributionPackage == null) {
distributionPackage = readPackageInternal(resourceResolver, stream);
}
distributionPackage.getInfo().putAll(headerInfo);
return distributionPackage;
}
public boolean installPackage(@Nonnull ResourceResolver resourceResolver, @Nonnull DistributionPackage distributionPackage) throws DistributionException {
DistributionRequestType actionType = distributionPackage.getInfo().getRequestType();
if (!type.equals(distributionPackage.getType())) {
throw new DistributionException("not supported package type" + distributionPackage.getType());
}
boolean installed = false;
if (DistributionRequestType.DELETE.equals(actionType)) {
installed = installDeletePackage(resourceResolver, distributionPackage);
} else if (DistributionRequestType.TEST.equals(actionType)) {
// do nothing for test packages
installed = true;
} else if (DistributionRequestType.ADD.equals(actionType)) {
installed = installAddPackage(resourceResolver, distributionPackage);
}
return installed;
}
@Nonnull
@Override
public DistributionPackageInfo installPackage(@Nonnull ResourceResolver resourceResolver, @Nonnull InputStream stream) throws DistributionException {
if (!stream.markSupported()) {
stream = new BufferedInputStream(stream);
}
DistributionPackageInfo packageInfo = new DistributionPackageInfo(type);
DistributionPackageUtils.readInfo(stream, packageInfo);
DistributionPackage distributionPackage = SimpleDistributionPackage.fromStream(stream, type);
boolean installed;
// not a simple package
if (distributionPackage == null) {
installed = installPackageInternal(resourceResolver, stream);
} else {
installed = installPackage(resourceResolver, distributionPackage);
packageInfo.putAll(distributionPackage.getInfo());
}
if (installed) {
return packageInfo;
} else {
throw new DistributionException("could not install package from stream");
}
}
private boolean installDeletePackage(@Nonnull ResourceResolver resourceResolver, @CheckForNull DistributionPackage distributionPackage) throws DistributionException {
Session session = null;
try {
if (distributionPackage != null) {
session = getSession(resourceResolver);
for (String path : distributionPackage.getInfo().getPaths()) {
if (session.itemExists(path)) {
session.removeItem(path);
}
}
return true;
}
} catch (Exception e) {
throw new DistributionException(e);
} finally {
ungetSession(session);
}
return false;
}
private boolean installAddPackage(@Nonnull ResourceResolver resourceResolver, @Nonnull DistributionPackage distributionPackage)
throws DistributionException {
InputStream inputStream = null;
try {
inputStream = distributionPackage.createInputStream();
return installPackageInternal(resourceResolver, inputStream);
} catch (IOException e) {
throw new DistributionException(e);
} finally {
IOUtils.closeQuietly(inputStream);
}
}
@CheckForNull
public DistributionPackage getPackage(@Nonnull ResourceResolver resourceResolver, @Nonnull String id) {
DistributionPackage distributionPackage = SimpleDistributionPackage.fromIdString(id, type);
// not a simple package
if (distributionPackage == null) {
if (id.startsWith("reference")) {
String localId = id.substring("reference-".length());
distributionPackage = new ReferencePackage(getPackageInternal(resourceResolver, localId));
} else {
distributionPackage = getPackageInternal(resourceResolver, id);
}
}
return distributionPackage;
}
private Session getSession(ResourceResolver resourceResolver) throws RepositoryException {
Session session = resourceResolver.adaptTo(Session.class);
if (session != null) {
// this is needed in order to avoid loops in sync case when there're deletions, otherwise it could work with sling resources
DistributionJcrUtils.setDoNotDistribute(session);
} else {
throw new RepositoryException("could not obtain a session from calling user " + resourceResolver.getUserID());
}
return session;
}
private void ungetSession(Session session) {
if (session != null) {
try {
if (session.hasPendingChanges()) {
session.save();
}
} catch (RepositoryException e) {
log.debug("Cannot save session", e);
}
}
}
@CheckForNull
protected abstract DistributionPackage createPackageForAdd(@Nonnull ResourceResolver resourceResolver, @Nonnull DistributionRequest request)
throws DistributionException;
@CheckForNull
protected abstract DistributionPackage readPackageInternal(@Nonnull ResourceResolver resourceResolver, @Nonnull InputStream stream)
throws DistributionException;
protected abstract boolean installPackageInternal(@Nonnull ResourceResolver resourceResolver, @Nonnull InputStream stream)
throws DistributionException;
@CheckForNull
protected abstract DistributionPackage getPackageInternal(@Nonnull ResourceResolver resourceResolver, @Nonnull String id);
}