/*
* 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.core.typereg;
import java.io.StringReader;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.brooklyn.api.entity.Application;
import org.apache.brooklyn.api.entity.EntitySpec;
import org.apache.brooklyn.api.internal.AbstractBrooklynObjectSpec;
import org.apache.brooklyn.api.typereg.RegisteredType;
import org.apache.brooklyn.api.typereg.RegisteredTypeLoadingContext;
import org.apache.brooklyn.entity.stock.BasicApplication;
import org.apache.brooklyn.entity.stock.BasicEntity;
import org.apache.brooklyn.util.core.xstream.XmlSerializer;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.stream.ReaderInputStream;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
/** Example implementation of {@link BrooklynTypePlanTransformer} showing
* how implementations are meant to be written. */
public class ExampleXmlTypePlanTransformer extends AbstractTypePlanTransformer {
protected ExampleXmlTypePlanTransformer() {
super("example-xml", "Example XML", "Illustration of writing a transformer");
}
@Override
protected double scoreForNullFormat(Object planData, RegisteredType type, RegisteredTypeLoadingContext context) {
if (!(planData instanceof String)) return 0;
try {
// if it's XML, accept it
parseXml((String)planData);
return 0.3;
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
return 0;
}
}
@Override
protected double scoreForNonmatchingNonnullFormat(String planFormat, Object planData, RegisteredType type, RegisteredTypeLoadingContext context) {
// only null and xml supported
return 0;
}
@Override
protected AbstractBrooklynObjectSpec<?, ?> createSpec(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
return toEntitySpec(parseXml((String)type.getPlan().getPlanData()),
isApplicationExpected(type, context) ? 0 : 1);
}
private static boolean isApplicationExpected(RegisteredType type, RegisteredTypeLoadingContext context) {
return RegisteredTypes.isSubtypeOf(type, Application.class) ||
(context.getExpectedJavaSuperType()!=null && context.getExpectedJavaSuperType().isAssignableFrom(Application.class));
}
@Override
protected Object createBean(RegisteredType type, RegisteredTypeLoadingContext context) throws Exception {
return new XmlSerializer<Object>().fromString((String)type.getPlan().getPlanData());
}
@Override
public double scoreForTypeDefinition(String formatCode, Object catalogData) {
// defining types not supported
return 0;
}
@Override
public List<RegisteredType> createFromTypeDefinition(String formatCode, Object catalogData) {
// defining types not supported
return null;
}
private Document parseXml(String plan) {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
Document dom;
try {
//Using factory get an instance of document builder
DocumentBuilder db = dbf.newDocumentBuilder();
//parse using builder to get DOM representation of the XML file
dom = db.parse(new ReaderInputStream(new StringReader(plan)));
} catch (Exception e) {
Exceptions.propagateIfFatal(e);
throw new UnsupportedTypePlanException(e);
}
return dom;
}
private EntitySpec<?> toEntitySpec(Node dom, int depth) {
if (dom.getNodeType()==Node.DOCUMENT_NODE) {
if (dom.getChildNodes().getLength()!=1) {
// NB: <?...?> entity preamble might break this
throw new IllegalStateException("Document for "+dom+" has "+dom.getChildNodes().getLength()+" nodes; 1 expected.");
}
return toEntitySpec(dom.getChildNodes().item(0), depth);
}
EntitySpec<?> result = depth == 0 ? EntitySpec.create(BasicApplication.class) : EntitySpec.create(BasicEntity.class);
result.displayName(dom.getNodeName());
if (dom.getAttributes()!=null) {
for (int i=0; i<dom.getAttributes().getLength(); i++)
result.configure(dom.getAttributes().item(i).getNodeName(), dom.getAttributes().item(i).getTextContent());
}
if (dom.getChildNodes()!=null) {
for (int i=0; i<dom.getChildNodes().getLength(); i++) {
Node item = dom.getChildNodes().item(i);
if (item.getNodeType()==Node.ELEMENT_NODE) {
result.child(toEntitySpec(item, depth+1));
}
}
}
return result;
}
}