84a3e28e40d1a772f0f427d2f75e61026dac4f12
[vfc/nfvo/wfengine.git] /
1 /*******************************************************************************
2  * Copyright (c) 2013 Pascal Hirmer.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * and the Apache License 2.0 which both accompany this distribution,
6  * and are available at http://www.eclipse.org/legal/epl-v10.html
7  * and http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Contributors:
10  *    Pascal Hirmer - initial API and implementation
11  *******************************************************************************/
12
13 package org.eclipse.winery.topologymodeler.addons.topologycompleter.helper;
14
15 import java.io.IOException;
16 import java.io.StringReader;
17 import java.io.StringWriter;
18 import java.util.ArrayList;
19 import java.util.List;
20
21 import javax.xml.bind.JAXBContext;
22 import javax.xml.bind.JAXBElement;
23 import javax.xml.bind.JAXBException;
24 import javax.xml.bind.Marshaller;
25 import javax.xml.bind.Unmarshaller;
26
27 import org.eclipse.winery.common.ModelUtilities;
28 import org.eclipse.winery.common.Util;
29 import org.eclipse.winery.model.tosca.Definitions;
30 import org.eclipse.winery.model.tosca.TDefinitions;
31 import org.eclipse.winery.model.tosca.TEntityTemplate;
32 import org.eclipse.winery.model.tosca.TNodeTemplate;
33 import org.eclipse.winery.model.tosca.TRelationshipTemplate;
34 import org.eclipse.winery.model.tosca.TRelationshipTemplate.SourceElement;
35 import org.eclipse.winery.model.tosca.TServiceTemplate;
36 import org.eclipse.winery.model.tosca.TTopologyTemplate;
37 import org.slf4j.LoggerFactory;
38
39 import com.fasterxml.jackson.databind.ObjectMapper;
40 import com.fasterxml.jackson.databind.type.TypeFactory;
41
42 /**
43  * This class contains methods for marshalling and unmarshalling a topology XML string via JAXB.
44  *
45  */
46 public class JAXBHelper {
47
48         private static final org.slf4j.Logger logger = LoggerFactory.getLogger(JAXBHelper.class.getName());
49
50         /**
51          * This constant is used in the buildXML method which add coordinates to Node Templates so they
52          * are arranged properly in the Winery topology modeler.
53          *
54          * The x coordinate is constant because it is assumed that a stack of NodeTemplates is displayed.
55          */
56         private static final String NODETEMPLATE_X_COORDINATE = "500";
57
58         /**
59          * This method creates an JAXB Unmarshaller used by the methods contained in this class.
60          *
61          * @return the JAXB unmarshaller object
62          *
63          * @throws JAXBException
64          *             this exception can occur when the JAXBContext is created
65          */
66         private static Unmarshaller createUnmarshaller() throws JAXBException {
67                 // initiate JaxB context
68                 JAXBContext context;
69                 context = JAXBContext.newInstance(Definitions.class);
70
71                 return context.createUnmarshaller();
72         }
73
74         /**
75          * This method returns a {@link TTopologyTemplate} given as XML string as JaxBObject.
76          *
77          * @param xmlString
78          *            the {@link TTopologyTemplate} to be unmarshalled
79          *
80          * @return the unmarshalled {@link TTopologyTemplate}
81          */
82         public static TTopologyTemplate getTopologyAsJaxBObject(String xmlString) {
83                 try {
84
85                         logger.info("Getting Definitions Document...");
86
87                         StringReader reader = new StringReader(xmlString);
88
89                         // unmarshall the XML string
90                         Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader);
91                         TServiceTemplate serviceTemplate = (TServiceTemplate) jaxBDefinitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0);
92
93                         logger.info("Unmarshalling successful! ");
94
95                         return serviceTemplate.getTopologyTemplate();
96
97                 } catch (JAXBException e) {
98                         logger.error(e.getLocalizedMessage());
99                 }
100                 return null;
101         }
102
103         /**
104          * This method returns {@link TRelationshipTemplate}s as a JaxBObject.
105          *
106          * @param xmlString
107          *            the {@link TRelationshipTemplate} to be unmarshalled
108          *
109          * @return the unmarshalled {@link TRelationshipTemplate}
110          */
111         public static List<TRelationshipTemplate> getRelationshipTemplatesAsJaxBObject(String xmlString) {
112                 try {
113                         StringReader reader = new StringReader(xmlString);
114
115                         // unmarshall
116                         Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader);
117                         TServiceTemplate serviceTemplate = (TServiceTemplate) jaxBDefinitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0);
118
119                         List<TRelationshipTemplate> foundRTs = new ArrayList<>();
120                         for (TEntityTemplate entity : serviceTemplate.getTopologyTemplate().getNodeTemplateOrRelationshipTemplate()) {
121                                 if (entity instanceof TRelationshipTemplate) {
122                                         foundRTs.add((TRelationshipTemplate) entity);
123                                 }
124                         }
125
126                         return foundRTs;
127
128                 } catch (JAXBException e) {
129                         logger.error(e.getLocalizedMessage());
130                 }
131                 return null;
132
133         }
134
135         /**
136          * Turns XML Strings into {@link TEntityTemplate} objects using JaxB.
137          *
138          * @param xmlString
139          *            the XMLString to be parsed
140          * @return the parsed XMLString as {@link TEntityTemplate}
141          */
142         public static List<TEntityTemplate> getEntityTemplatesAsJaxBObject(String xmlString) {
143                 try {
144                         StringReader reader = new StringReader(xmlString);
145
146                         Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader);
147                         TServiceTemplate serviceTemplate = (TServiceTemplate) jaxBDefinitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().get(0);
148
149                         return serviceTemplate.getTopologyTemplate().getNodeTemplateOrRelationshipTemplate();
150
151                 } catch (JAXBException e) {
152                         logger.error(e.getLocalizedMessage());
153                 }
154                 return null;
155
156         }
157
158         /**
159          * Converts any object of the TOSCA data model to a JaxBObject.
160          *
161          * @param xmlString
162          *            the {@link Definitions} object to be converted
163          *
164          * @return the unmarshalled {@link Definitions} object
165          */
166         public static Definitions getXasJaxBObject(String xmlString) {
167                 try {
168                         StringReader reader = new StringReader(xmlString);
169                         Definitions jaxBDefinitions = (Definitions) createUnmarshaller().unmarshal(reader);
170
171                         return jaxBDefinitions;
172
173                 } catch (JAXBException e) {
174                         logger.error(e.getLocalizedMessage());
175                 }
176                 return null;
177
178         }
179
180         /**
181          * This method adds a selection of {@link TNodeTemplate}- and {@link TRelationshipTemplate}-XML-Strings to a {@link TTopologyTemplate}-XML-String using JAXB.
182          * After the templates have been added, the {@link TTopologyTemplate} object is re-marshalled to an XML-String.
183          *
184          * This method is called by the selectionHandler.jsp after several Node or RelationshipTemplates have been chosen in a dialog.
185          *
186          * @param topology
187          *            the topology as XML string
188          * @param allTemplateChoicesAsXML
189          *            all possible template choices as TOSCA-XML strings containing the complete templates
190          * @param selectedNodeTemplatesAsJSON
191          *            the names of the selected NodeTemplates as JSONArray
192          * @param selectedRelationshipTemplatesAsJSON
193          *            the names of the selected RelationshipTemplates as JSONArray
194          *
195          * @return the complete topology XML string
196          */
197         public static String addTemplatesToTopology(String topology, String allTemplateChoicesAsXML, String selectedNodeTemplatesAsJSON, String selectedRelationshipTemplatesAsJSON) {
198                 try {
199
200                         // initialization code for the jackson types used to convert JSON string arrays to a java.util.List
201                         ObjectMapper mapper = new ObjectMapper();
202                         TypeFactory factory = mapper.getTypeFactory();
203
204                         // convert the JSON array containing the names of the selected RelationshipTemplates to a java.util.List
205                         List<String> selectedRelationshipTemplates = mapper.readValue(selectedRelationshipTemplatesAsJSON, factory.constructCollectionType(List.class, String.class));
206
207                         // convert the topology and the choices to objects using JAXB
208                         TTopologyTemplate topologyTemplate = getTopologyAsJaxBObject(topology);
209                         List<TEntityTemplate> allTemplateChoices = getEntityTemplatesAsJaxBObject(allTemplateChoicesAsXML);
210
211                         // this distinction of cases is necessary because it is possible that only RelationshipTemplates have been selected
212                         if (selectedNodeTemplatesAsJSON != null) {
213
214                                 // convert the JSON string array containing the names of the selected NodeTemplates to a java.util.List
215                                 List<String> selectedNodeTemplates = mapper.readValue(selectedNodeTemplatesAsJSON, factory.constructCollectionType(List.class, String.class));
216
217                                 // search the selected NodeTemplate in the List of all choices by its name to receive its object which will ne added to the topology
218                                 for (String nodeTemplateName : selectedNodeTemplates) {
219                                         for (TEntityTemplate choice : allTemplateChoices) {
220                                                 if (choice instanceof TNodeTemplate) {
221                                                         TNodeTemplate nodeTemplate = (TNodeTemplate) choice;
222                                                         // matching a name is usually unsafe because the uniqueness cannot be assured,
223                                                         // however similar names are not possible at this location due to the implementation of the selection dialogs
224                                                         if (nodeTemplateName.equals(nodeTemplate.getName())) {
225                                                                 // add the selected NodeTemplate to the topology
226                                                                 topologyTemplate.getNodeTemplateOrRelationshipTemplate().add(nodeTemplate);
227
228                                                                 // due to the mapping of IDs in the selection dialog, the corresponding Relationship Template of the inserted Node Template misses its SourceElement.
229                                                                 // Re-add it to avoid errors.
230                                                                 for (TEntityTemplate entity: topologyTemplate.getNodeTemplateOrRelationshipTemplate()) {
231                                                                         if (entity instanceof TRelationshipTemplate) {
232                                                                                 TRelationshipTemplate relationshipTemplate = (TRelationshipTemplate) entity;
233                                                                                 if (relationshipTemplate.getSourceElement().getRef() == null) {
234                                                                                         // connect to the added NodeTemplate
235                                                                                         SourceElement sourceElement = new SourceElement();
236                                                                                         sourceElement.setRef(nodeTemplate);
237                                                                                         relationshipTemplate.setSourceElement(sourceElement);
238                                                                                 }
239                                                                         }
240                                                                 }
241                                                         }
242                                                 }
243                                         }
244                                 }
245
246                                 // now search and add the selected RelationshipTemplate object connecting to the inserted NodeTemplate
247                                 for (String relationshipTemplateName : selectedRelationshipTemplates) {
248                                         for (TEntityTemplate toBeAdded : allTemplateChoices) {
249                                                 if (toBeAdded instanceof TRelationshipTemplate) {
250                                                         TRelationshipTemplate relationshipTemplate = (TRelationshipTemplate) toBeAdded;
251                                                         if (relationshipTemplateName.equals(relationshipTemplate.getName())) {
252                                                                 topologyTemplate.getNodeTemplateOrRelationshipTemplate().add(relationshipTemplate);
253                                                         }
254                                                 }
255                                         }
256                                 }
257
258                         } else {
259                                 // in this case only Relationship Templates have been selected
260                                 List<TRelationshipTemplate> allRelationshipTemplateChoices = JAXBHelper.getRelationshipTemplatesAsJaxBObject(allTemplateChoicesAsXML);
261
262                                 // add the target Node Template to the topology which is unique due to the implementation of the selection dialog
263                                 topologyTemplate.getNodeTemplateOrRelationshipTemplate().add((TNodeTemplate) ((TRelationshipTemplate) allRelationshipTemplateChoices.get(0)).getTargetElement().getRef());
264
265                                 // search the JAXB object of the selected RelationshipTemplate and add it to the topology
266                                 for (String relationshipTemplateName : selectedRelationshipTemplates) {
267                                         for (TRelationshipTemplate choice : allRelationshipTemplateChoices) {
268                                                 if (relationshipTemplateName.equals(choice.getName())) {
269                                                         topologyTemplate.getNodeTemplateOrRelationshipTemplate().add(choice);
270                                                 }
271                                         }
272                                 }
273
274                                 for (TEntityTemplate entityTemplate : topologyTemplate.getNodeTemplateOrRelationshipTemplate()) {
275                                         if (entityTemplate instanceof TRelationshipTemplate) {
276                                                 TRelationshipTemplate relationship = (TRelationshipTemplate) entityTemplate;
277
278                                                 // due to the mapping of IDs in the selection dialog, the corresponding Relationship Template of the inserted Node Template misses its SourceElement.
279                                                 // Re-add it to avoid errors.
280                                                 if (relationship.getSourceElement().getRef() == null) {
281                                                         relationship.getSourceElement().setRef((TNodeTemplate) ((TRelationshipTemplate) allRelationshipTemplateChoices.get(0)).getTargetElement().getRef());
282                                                 }
283                                         }
284                                 }
285                         }
286
287                         // re-convert the topology from a JAXB object to an XML string and return it
288                         Definitions definitions = new Definitions();
289                         TServiceTemplate st = new TServiceTemplate();
290                         st.setTopologyTemplate(topologyTemplate);
291                         definitions.getServiceTemplateOrNodeTypeOrNodeTypeImplementation().add(st);
292                         JAXBContext context = JAXBContext.newInstance(Definitions.class);
293                         Marshaller m = context.createMarshaller();
294                         StringWriter stringWriter = new StringWriter();
295
296                         m.marshal(definitions, stringWriter);
297
298                         return stringWriter.toString();
299
300                 } catch (JAXBException | IOException e) {
301                         logger.error(e.getLocalizedMessage());
302                 }
303
304                 return null;
305         }
306
307
308         /**
309          * Marshalls a JAXB object of the TOSCA model to an XML string.
310          *
311          * @param clazz
312          *                       the class of the object
313          * @param obj
314          *                       the object to be marshalled
315          *
316          * @return
317          */
318         public static String getXMLAsString(@SuppressWarnings("rawtypes") Class clazz, Object obj) {
319                 try {
320                         @SuppressWarnings("rawtypes")
321                         JAXBElement rootElement = Util.getJAXBElement(clazz, obj);
322                         JAXBContext context = JAXBContext.newInstance(TDefinitions.class);
323                         Marshaller m;
324
325                         m = context.createMarshaller();
326
327                         StringWriter w = new StringWriter();
328                         m.marshal(rootElement, w);
329                         String res = w.toString();
330
331                         return res;
332                 } catch (JAXBException e) {
333                         logger.error(e.getLocalizedMessage());
334                 }
335                 return null;
336         }
337
338         /**
339          * 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
340          * Winery-Modeler-UI.
341          *
342          * This code is adapted from the org.eclipse.winery.repository.Utils.getXMLAsString() method.
343          *
344          * @param topology
345          *            the {@link TTopologyTemplate} to be altered
346          *
347          * @return the altered {@link TTopologyTemplate}
348          */
349         public static TTopologyTemplate buildXML(TTopologyTemplate topology) {
350
351                 // the coordinate of the NodeTemplate in Winery. Begin 100 pixel from the top to improve arrangement.
352                 int yCoordinates = 100;
353
354                 for (TEntityTemplate template : topology.getNodeTemplateOrRelationshipTemplate()) {
355                         // add node templates
356                         if (template instanceof TNodeTemplate) {
357
358                                 TNodeTemplate nodeTemplate = (TNodeTemplate) template;
359
360                                 // remove the Requirements tag if necessary
361                                 if (nodeTemplate.getRequirements() != null && nodeTemplate.getRequirements().getRequirement() == null) {
362                                         nodeTemplate.setRequirements(null);
363                                 }
364
365                                 ModelUtilities.setLeft(nodeTemplate, NODETEMPLATE_X_COORDINATE);
366                                 ModelUtilities.setTop(nodeTemplate, Integer.toString(yCoordinates));
367
368                                 yCoordinates += 150;
369                         }
370                 }
371
372                 return topology;
373         }
374 }