Java tutorial
/******************************************************************************* * Copyright (c) 2013 Pascal Hirmer. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * and the Apache License 2.0 which both accompany this distribution, * and are available at http://www.eclipse.org/legal/epl-v10.html * and http://www.apache.org/licenses/LICENSE-2.0 * * Contributors: * Pascal Hirmer - initial API and implementation *******************************************************************************/ package org.eclipse.winery.topologymodeler.addons.topologycompleter.helper; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import javax.xml.bind.Unmarshaller; import org.eclipse.winery.common.ModelUtilities; import org.eclipse.winery.common.Util; import org.eclipse.winery.model.tosca.Definitions; import org.eclipse.winery.model.tosca.TDefinitions; import org.eclipse.winery.model.tosca.TEntityTemplate; import org.eclipse.winery.model.tosca.TNodeTemplate; import org.eclipse.winery.model.tosca.TRelationshipTemplate; import org.eclipse.winery.model.tosca.TRelationshipTemplate.SourceElement; import org.eclipse.winery.model.tosca.TServiceTemplate; import org.eclipse.winery.model.tosca.TTopologyTemplate; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.type.TypeFactory; /** * This class contains methods for marshalling and unmarshalling a topology XML string via JAXB. * */ public class JAXBHelper { private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JAXBHelper.class.getName()); /** * This constant is used in the buildXML method which add coordinates to Node Templates so they * are arranged properly in the Winery topology modeler. * * The x coordinate is constant because it is assumed that a stack of NodeTemplates is displayed. */ private static final String NODETEMPLATE_X_COORDINATE = "500"; /** * This method creates an JAXB Unmarshaller used by the methods contained in this class. * * @return the JAXB unmarshaller object * * @throws JAXBException * this exception can occur when the JAXBContext is created */ private static Unmarshaller createUnmarshaller() throws JAXBException { // initiate JaxB context JAXBContext context; context = JAXBContext.newInstance(Definitions.class); return context.createUnmarshaller(); } /** * This method returns a {@link TTopologyTemplate} given as XML string as JaxBObject. * * @param xmlString * the {@link TTopologyTemplate} to be unmarshalled * * @return the unmarshalled {@link TTopologyTemplate} */ public static TTopologyTemplate getTopologyAsJaxBObject(String xmlString) { try { logger.info("Getting Definitions Document..."); StringReader reader = new StringReader(xmlString); // unmarshall the XML string Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader); TServiceTemplate serviceTemplate = (TServiceTemplate) jaxBDefinitions .getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0); logger.info("Unmarshalling successful! "); return serviceTemplate.getTopologyTemplate(); } catch (JAXBException e) { logger.error(e.getLocalizedMessage()); } return null; } /** * This method returns {@link TRelationshipTemplate}s as a JaxBObject. * * @param xmlString * the {@link TRelationshipTemplate} to be unmarshalled * * @return the unmarshalled {@link TRelationshipTemplate} */ public static List<TRelationshipTemplate> getRelationshipTemplatesAsJaxBObject(String xmlString) { try { StringReader reader = new StringReader(xmlString); // unmarshall Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader); TServiceTemplate serviceTemplate = (TServiceTemplate) jaxBDefinitions .getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0); List<TRelationshipTemplate> foundRTs = new ArrayList<>(); for (TEntityTemplate entity : serviceTemplate.getTopologyTemplate() .getNodeTemplateOrRelationshipTemplate()) { if (entity instanceof TRelationshipTemplate) { foundRTs.add((TRelationshipTemplate) entity); } } return foundRTs; } catch (JAXBException e) { logger.error(e.getLocalizedMessage()); } return null; } /** * Turns XML Strings into {@link TEntityTemplate} objects using JaxB. * * @param xmlString * the XMLString to be parsed * @return the parsed XMLString as {@link TEntityTemplate} */ public static List<TEntityTemplate> getEntityTemplatesAsJaxBObject(String xmlString) { try { StringReader reader = new StringReader(xmlString); Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader); TServiceTemplate serviceTemplate = (TServiceTemplate) jaxBDefinitions .getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0); return serviceTemplate.getTopologyTemplate().getNodeTemplateOrRelationshipTemplate(); } catch (JAXBException e) { logger.error(e.getLocalizedMessage()); } return null; } /** * Converts any object of the TOSCA data model to a JaxBObject. * * @param xmlString * the {@link Definitions} object to be converted * * @return the unmarshalled {@link Definitions} object */ public static Definitions getXasJaxBObject(String xmlString) { try { StringReader reader = new StringReader(xmlString); Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader); return jaxBDefinitions; } catch (JAXBException e) { logger.error(e.getLocalizedMessage()); } return null; } /** * This method adds a selection of {@link TNodeTemplate}- and {@link TRelationshipTemplate}-XML-Strings to a {@link TTopologyTemplate}-XML-String using JAXB. * After the templates have been added, the {@link TTopologyTemplate} object is re-marshalled to an XML-String. * * This method is called by the selectionHandler.jsp after several Node or RelationshipTemplates have been chosen in a dialog. * * @param topology * the topology as XML string * @param allTemplateChoicesAsXML * all possible template choices as TOSCA-XML strings containing the complete templates * @param selectedNodeTemplatesAsJSON * the names of the selected NodeTemplates as JSONArray * @param selectedRelationshipTemplatesAsJSON * the names of the selected RelationshipTemplates as JSONArray * * @return the complete topology XML string */ public static String addTemplatesToTopology(String topology, String allTemplateChoicesAsXML, String selectedNodeTemplatesAsJSON, String selectedRelationshipTemplatesAsJSON) { try { // initialization code for the jackson types used to convert JSON string arrays to a java.util.List ObjectMapper mapper = new ObjectMapper(); TypeFactory factory = mapper.getTypeFactory(); // convert the JSON array containing the names of the selected RelationshipTemplates to a java.util.List List<String> selectedRelationshipTemplates = mapper.readValue(selectedRelationshipTemplatesAsJSON, factory.constructCollectionType(List.class, String.class)); // convert the topology and the choices to objects using JAXB TTopologyTemplate topologyTemplate = getTopologyAsJaxBObject(topology); List<TEntityTemplate> allTemplateChoices = getEntityTemplatesAsJaxBObject(allTemplateChoicesAsXML); // this distinction of cases is necessary because it is possible that only RelationshipTemplates have been selected if (selectedNodeTemplatesAsJSON != null) { // convert the JSON string array containing the names of the selected NodeTemplates to a java.util.List List<String> selectedNodeTemplates = mapper.readValue(selectedNodeTemplatesAsJSON, factory.constructCollectionType(List.class, String.class)); // search the selected NodeTemplate in the List of all choices by its name to receive its object which will ne added to the topology for (String nodeTemplateName : selectedNodeTemplates) { for (TEntityTemplate choice : allTemplateChoices) { if (choice instanceof TNodeTemplate) { TNodeTemplate nodeTemplate = (TNodeTemplate) choice; // matching a name is usually unsafe because the uniqueness cannot be assured, // however similar names are not possible at this location due to the implementation of the selection dialogs if (nodeTemplateName.equals(nodeTemplate.getName())) { // add the selected NodeTemplate to the topology topologyTemplate.getNodeTemplateOrRelationshipTemplate().add(nodeTemplate); // due to the mapping of IDs in the selection dialog, the corresponding Relationship Template of the inserted Node Template misses its SourceElement. // Re-add it to avoid errors. for (TEntityTemplate entity : topologyTemplate .getNodeTemplateOrRelationshipTemplate()) { if (entity instanceof TRelationshipTemplate) { TRelationshipTemplate relationshipTemplate = (TRelationshipTemplate) entity; if (relationshipTemplate.getSourceElement().getRef() == null) { // connect to the added NodeTemplate SourceElement sourceElement = new SourceElement(); sourceElement.setRef(nodeTemplate); relationshipTemplate.setSourceElement(sourceElement); } } } } } } } // now search and add the selected RelationshipTemplate object connecting to the inserted NodeTemplate for (String relationshipTemplateName : selectedRelationshipTemplates) { for (TEntityTemplate toBeAdded : allTemplateChoices) { if (toBeAdded instanceof TRelationshipTemplate) { TRelationshipTemplate relationshipTemplate = (TRelationshipTemplate) toBeAdded; if (relationshipTemplateName.equals(relationshipTemplate.getName())) { topologyTemplate.getNodeTemplateOrRelationshipTemplate().add(relationshipTemplate); } } } } } else { // in this case only Relationship Templates have been selected List<TRelationshipTemplate> allRelationshipTemplateChoices = JAXBHelper .getRelationshipTemplatesAsJaxBObject(allTemplateChoicesAsXML); // add the target Node Template to the topology which is unique due to the implementation of the selection dialog topologyTemplate.getNodeTemplateOrRelationshipTemplate() .add((TNodeTemplate) ((TRelationshipTemplate) allRelationshipTemplateChoices.get(0)) .getTargetElement().getRef()); // search the JAXB object of the selected RelationshipTemplate and add it to the topology for (String relationshipTemplateName : selectedRelationshipTemplates) { for (TRelationshipTemplate choice : allRelationshipTemplateChoices) { if (relationshipTemplateName.equals(choice.getName())) { topologyTemplate.getNodeTemplateOrRelationshipTemplate().add(choice); } } } for (TEntityTemplate entityTemplate : topologyTemplate.getNodeTemplateOrRelationshipTemplate()) { if (entityTemplate instanceof TRelationshipTemplate) { TRelationshipTemplate relationship = (TRelationshipTemplate) entityTemplate; // due to the mapping of IDs in the selection dialog, the corresponding Relationship Template of the inserted Node Template misses its SourceElement. // Re-add it to avoid errors. if (relationship.getSourceElement().getRef() == null) { relationship.getSourceElement().setRef( (TNodeTemplate) ((TRelationshipTemplate) allRelationshipTemplateChoices.get(0)) .getTargetElement().getRef()); } } } } // re-convert the topology from a JAXB object to an XML string and return it Definitions definitions = new Definitions(); TServiceTemplate st = new TServiceTemplate(); st.setTopologyTemplate(topologyTemplate); definitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().add(st); JAXBContext context = JAXBContext.newInstance(Definitions.class); Marshaller m = context.createMarshaller(); StringWriter stringWriter = new StringWriter(); m.marshal(definitions, stringWriter); return stringWriter.toString(); } catch (JAXBException | IOException e) { logger.error(e.getLocalizedMessage()); } return null; } /** * Marshalls a JAXB object of the TOSCA model to an XML string. * * @param clazz * the class of the object * @param obj * the object to be marshalled * * @return */ public static String getXMLAsString(@SuppressWarnings("rawtypes") Class clazz, Object obj) { try { @SuppressWarnings("rawtypes") JAXBElement rootElement = Util.getJAXBElement(clazz, obj); JAXBContext context = JAXBContext.newInstance(TDefinitions.class); Marshaller m; m = context.createMarshaller(); StringWriter w = new StringWriter(); m.marshal(rootElement, w); String res = w.toString(); return res; } catch (JAXBException e) { logger.error(e.getLocalizedMessage()); } return null; } /** * This methods alters the XML with JAXB so it can be imported in Winery. This is necessary because Winery needs additional information for the position of the templates in the * Winery-Modeler-UI. * * This code is adapted from the org.eclipse.winery.repository.Utils.getXMLAsString() method. * * @param topology * the {@link TTopologyTemplate} to be altered * * @return the altered {@link TTopologyTemplate} */ public static TTopologyTemplate buildXML(TTopologyTemplate topology) { // the coordinate of the NodeTemplate in Winery. Begin 100 pixel from the top to improve arrangement. int yCoordinates = 100; for (TEntityTemplate template : topology.getNodeTemplateOrRelationshipTemplate()) { // add node templates if (template instanceof TNodeTemplate) { TNodeTemplate nodeTemplate = (TNodeTemplate) template; // remove the Requirements tag if necessary if (nodeTemplate.getRequirements() != null && nodeTemplate.getRequirements().getRequirement() == null) { nodeTemplate.setRequirements(null); } ModelUtilities.setLeft(nodeTemplate, NODETEMPLATE_X_COORDINATE); ModelUtilities.setTop(nodeTemplate, Integer.toString(yCoordinates)); yCoordinates += 150; } } return topology; } }