/*
* 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.brooklyn.camp.spi.resolve;
import java.io.InputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.brooklyn.camp.CampPlatform;
import org.apache.brooklyn.camp.spi.AssemblyTemplate;
import org.apache.brooklyn.camp.spi.instantiate.BasicAssemblyTemplateInstantiator;
import org.apache.brooklyn.camp.spi.pdp.Artifact;
import org.apache.brooklyn.camp.spi.pdp.AssemblyTemplateConstructor;
import org.apache.brooklyn.camp.spi.pdp.DeploymentPlan;
import org.apache.brooklyn.camp.spi.pdp.Service;
import org.apache.brooklyn.camp.spi.resolve.interpret.PlanInterpretationContext;
import org.apache.brooklyn.camp.spi.resolve.interpret.PlanInterpretationNode;
import org.apache.brooklyn.util.collections.MutableMap;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.stream.Streams;
import org.apache.brooklyn.util.yaml.Yamls;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.ArchiveStreamFactory;
import org.yaml.snakeyaml.error.YAMLException;
import com.google.common.annotations.VisibleForTesting;
public class PdpProcessor {
final CampPlatform campPlatform;
final List<PdpMatcher> matchers = new ArrayList<PdpMatcher>();
final List<PlanInterpreter> interpreters = new ArrayList<PlanInterpreter>();
public PdpProcessor(CampPlatform campPlatform) {
this.campPlatform = campPlatform;
}
public DeploymentPlan parseDeploymentPlan(Reader yaml) {
return parseDeploymentPlan(Streams.readFully(yaml));
}
@SuppressWarnings("unchecked")
public DeploymentPlan parseDeploymentPlan(String yaml) {
Iterable<Object> template = Yamls.parseAll(yaml);
Map<String, Object> dpRootUninterpreted = null;
try {
dpRootUninterpreted = Yamls.getAs(template, Map.class);
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
throw new YAMLException("Plan not in acceptable format: "+(e.getMessage()!=null ? e.getMessage() : ""+e), e);
}
Map<String, Object> dpRootInterpreted = applyInterpreters(dpRootUninterpreted);
return DeploymentPlan.of(dpRootInterpreted, yaml);
}
/** create and return an AssemblyTemplate based on the given DP (yaml) */
public AssemblyTemplate registerDeploymentPlan(Reader yaml) {
DeploymentPlan plan = parseDeploymentPlan(yaml);
return registerDeploymentPlan(plan);
}
/** applies matchers to the given deployment plan to create an assembly template */
public AssemblyTemplate registerDeploymentPlan(DeploymentPlan plan) {
AssemblyTemplateConstructor atc = new AssemblyTemplateConstructor(campPlatform);
if (plan.getName()!=null) atc.name(plan.getName());
if (plan.getDescription()!=null) atc.description(plan.getDescription());
if (plan.getSourceCode()!=null) atc.sourceCode(plan.getSourceCode());
// nothing done with origin just now...
if (plan.getServices()!=null) {
for (Service svc: plan.getServices()) {
applyMatchers(svc, atc);
}
}
if (plan.getArtifacts()!=null) {
for (Artifact art: plan.getArtifacts()) {
applyMatchers(art, atc);
}
}
Map<String, Object> attrs = plan.getCustomAttributes();
if (attrs!=null && !attrs.isEmpty()) {
Map<String, Object> customAttrs = attrs;
if (customAttrs.containsKey("id")) {
// id shouldn't be leaking to entities, see InternalEntityFactory.createEntityAndDescendantsUninitialized.
// If set it will go through to the spec because AbstractBrooklynObject has @SetFromFlag("id") on the id property.
// Follows logic in BrooklynEntityMatcher.apply(...).
customAttrs = MutableMap.copyOf(attrs);
customAttrs.put("planId", customAttrs.remove("id"));
}
atc.addCustomAttributes(customAttrs);
}
if (atc.getInstantiator()==null)
// set a default instantiator which just invokes the component's instantiators
// (or throws unsupported exceptions, currently!)
atc.instantiator(BasicAssemblyTemplateInstantiator.class);
return atc.commit();
}
public AssemblyTemplate registerPdpFromArchive(InputStream archiveInput) {
try {
ArchiveInputStream input = new ArchiveStreamFactory()
.createArchiveInputStream(archiveInput);
while (true) {
ArchiveEntry entry = input.getNextEntry();
if (entry==null) break;
// TODO unpack entry, create a space on disk holding the archive ?
}
// use yaml...
throw new UnsupportedOperationException("in progress");
} catch (Exception e) {
throw Exceptions.propagate(e);
}
}
// ----------------------------
public void addMatcher(PdpMatcher m) {
// TODO a list is a crude way to do matching ... but good enough to start
matchers.add(m);
}
public List<PdpMatcher> getMatchers() {
return matchers;
}
protected void applyMatchers(Object deploymentPlanItem, AssemblyTemplateConstructor atc) {
for (PdpMatcher matcher: getMatchers()) {
if (matcher.accepts(deploymentPlanItem)) {
// TODO first accepting is a crude way to do matching ... but good enough to start
if (matcher.apply(deploymentPlanItem, atc))
return;
}
}
throw new IllegalArgumentException("Deployment plan item cannot be matched. Please check your YAML. Item: "+deploymentPlanItem);
}
// ----------------------------
public void addInterpreter(PlanInterpreter interpreter) {
interpreters.add(interpreter);
}
/** returns a DeploymentPlan object which is the result of running the interpretation
* (with all interpreters) against the supplied deployment plan YAML object,
* essentially a post-parse processing step before matching */
@SuppressWarnings("unchecked")
@VisibleForTesting
public Map<String, Object> applyInterpreters(Map<String, ?> originalDeploymentPlan) {
PlanInterpretationNode interpretation = new PlanInterpretationNode(
new PlanInterpretationContext(originalDeploymentPlan, interpreters));
return (Map<String, Object>) interpretation.getNewValue();
}
}