View Javadoc

1   /*
2    Copyright (C) 2007 Grid Systems, S.A.
3   
4    This library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8   
9    This library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Lesser General Public License for more details.
13  
14   You should have received a copy of the GNU Lesser General Public
15   License along with this library; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
17  */
18  package com.gridsystems.nextgrid.api.builder;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.net.URI;
24  import java.net.URISyntaxException;
25  import java.net.URL;
26  import java.util.ArrayList;
27  import java.util.Collection;
28  import java.util.List;
29  import java.util.Map;
30  
31  import javax.xml.namespace.QName;
32  
33  import nextgrid.api.builder.BuildException;
34  import nextgrid.api.builder.POMBuilder;
35  import nextgrid.api.pom.ControlProcess;
36  import nextgrid.api.pom.Process;
37  import nextgrid.api.pom.ProcessFactory;
38  import nextgrid.api.pom.ProcessFactoryFinder;
39  import nextgrid.api.pom.Reference;
40  
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  
44  /**
45   * Base class for POMBuilder implementations.
46   *
47   * @param <T> The type of models to build
48   *
49   * @author Rodrigo Ruiz
50   */
51  public abstract class AbstractBuilder<T extends Object> implements POMBuilder<T> {
52  
53    /**
54     * Class logger.
55     */
56    protected static final Log LOG = LogFactory.getLog("PARSER");
57  
58    /**
59     * Namespace used for attributes storing profile (semantic) information.
60     */
61    protected static final String NS_PROFILE = "urn:nextgrid:profile";
62  
63    /**
64     * Builder model type.
65     */
66    private final Class<T> modelType;
67  
68    /**
69     * Implementation URI.
70     */
71    private final URI uri;
72  
73    /**
74     * Factory used for creating Process instances.
75     */
76    protected final ProcessFactory factory = ProcessFactoryFinder.getFactory();
77  
78    /**
79     * Creates a new instance.
80     *
81     * @param modelType The model type
82     * @param uri       The implementation URI
83     */
84    public AbstractBuilder(Class<T> modelType, String uri) {
85      this.modelType = modelType;
86      try {
87        this.uri = new URI("urn:nextgrid:owl-ws");
88      } catch (Exception e) {
89        throw new RuntimeException(e);
90      }
91    }
92  
93    /**
94     * {@inheritDoc}
95     */
96    public final Class<T> getModelType() {
97      return this.modelType;
98    }
99  
100   /**
101    * {@inheritDoc}
102    */
103   public final URI getURI() {
104     return this.uri;
105   }
106 
107   /**
108    * {@inheritDoc}
109    */
110   public final Process importFrom(File src) throws IOException, BuildException {
111     return importFrom(src.toURI());
112   }
113 
114   /**
115    * {@inheritDoc}
116    */
117   public final Process importFrom(URL src) throws IOException, BuildException {
118     try {
119       return importFrom(src.toURI());
120     } catch (URISyntaxException e) {
121       throw new BuildException("Bad URL", e);
122     }
123   }
124 
125   /**
126    * {@inheritDoc}
127    */
128   public final Process importFrom(URI src) throws IOException, BuildException {
129     List<T> list = loadFrom(src);
130     if (list == null || list.size() == 0) {
131       return null;
132     } else {
133       return toPOM(list.get(0));
134     }
135   }
136 
137   /**
138    * {@inheritDoc}
139    */
140   public final Process importFrom(InputStream is) throws IOException, BuildException {
141     List<T> list = loadFrom(is);
142     if (list == null || list.size() == 0) {
143       return null;
144     } else {
145       return toPOM(list.get(0));
146     }
147   }
148 
149   /**
150    * {@inheritDoc}
151    */
152   public final Collection<Process> importAllFrom(URI src)
153     throws IOException, BuildException {
154 
155     Collection<Process> candidates = new ArrayList<Process>();
156     List<T> wflows = loadFrom(src);
157 
158     if (wflows != null) {
159       for (T wflow : wflows) {
160         candidates.add(toPOM(wflow));
161       }
162     }
163     return candidates;
164   }
165 
166   /**
167    * {@inheritDoc}
168    */
169   public final Collection<Process> importAllFrom(InputStream is)
170     throws IOException, BuildException {
171     Collection<Process> candidates = new ArrayList<Process>();
172     List<T> wflows = loadFrom(is);
173 
174     if (wflows != null) {
175       for (T wflow : wflows) {
176         candidates.add(toPOM(wflow));
177       }
178     }
179     return candidates;
180   }
181 
182   /**
183    * Loads a workflow file.
184    *
185    * @param src The file to import
186    * @return The workflow root Process instance
187    * @throws IOException If an error occurs
188    */
189   protected abstract List<T> loadFrom(URI src) throws IOException;
190 
191   /**
192    * Loads a workflow file.
193    *
194    * @param src A stream to read from
195    * @return The workflow root Process instance
196    * @throws IOException If an error occurs
197    */
198   protected abstract List<T> loadFrom(InputStream src) throws IOException;
199 
200   /**
201    * Creates a reference of the specified type.
202    *
203    * @param type The reference type
204    * @return A reference
205    */
206   protected final Reference<?> createRef(Class<?> type) {
207     return TypeManager.createReference(type);
208   }
209 
210   /**
211    * Searches for a given input name into a process ascendants input mappings.
212    *
213    * @param p    The process where the search starts
214    * @param name The input name
215    * @return An existing input name that can be mapped to the specified one
216    */
217   protected final String findMappedInputName(Process p, String name) {
218     Process parent = p.getParent();
219     if (parent == null) {
220       return null;
221     } else {
222       for (Map.Entry<QName, String> entry : parent.getAttributes().entrySet()) {
223         if (entry.getValue().equals(name)) {
224           if (entry.getKey().getNamespaceURI().equals("input-map")) {
225             String localName = entry.getKey().getLocalPart();
226             LOG.debug("Found input map from " + name + " to " + localName);
227             return localName;
228           }
229         }
230       }
231       return findMappedInputName(parent, name);
232     }
233   }
234 
235   /**
236    * Finds out missing references and creates them.
237    *
238    * @param p The process to search
239    */
240   protected final void addMissingRefs(Process p) {
241     for (String name : p.getUsedInputNames()) {
242       if (p.getInput(name) == null) {
243         // Look for mapped inputs
244         String mappedName = findMappedInputName(p, name);
245         if (mappedName == null) {
246           LOG.debug("Found missing input: " + p + "#" + name);
247           LOG.debug("Input type: " + p.getInputType(name));
248           Reference<?> ref = createRef(p.getInputType(name));
249           LOG.debug("Created reference: " + ref.getClass());
250           p.putInput(name, ref);
251         } else {
252           Class<?> type = p.getInputType(name);
253           p.unuseInput(name);
254           p.useInput(mappedName, type);
255 
256           QName qname = new QName("input-map", name);
257           String name2 = p.getAttribute(qname);
258           if (name2 != null) {
259             name = name2;
260             p.setAttribute(qname, null);
261           }
262           p.setAttribute(new QName("input-map", mappedName), name);
263         }
264       }
265     }
266 
267     for (String name : p.getUsedOutputNames()) {
268       if (p.getOutput(name) == null) {
269         LOG.debug("Found missing output: " + p + "#" + name);
270         p.putOutput(name, createRef(p.getOutputType(name)));
271       }
272     }
273 
274     if (p instanceof ControlProcess) {
275       for (Process child : ((ControlProcess)p).getChildren()) {
276         addMissingRefs(child);
277       }
278     }
279   }
280 
281   /**
282    * Binds a process output to another process input through a reference.
283    *
284    * @param src The source process
285    * @param dst The destination process
286    * @param srcParamId The source output
287    * @param dstParamId The destination input
288    */
289   protected final void bindOutputInput(Process src, Process dst,
290     String srcParamId, String dstParamId) {
291 
292     LOG.debug("Binding data-flow:");
293     LOG.debug("  - Source: " + src.getId() + "#" + srcParamId);
294     LOG.debug("  - Target: " + dst.getId() + "#" + dstParamId);
295 
296     Reference<?> ref = src.getOutput(srcParamId);
297     if (ref == null) {
298       ref = dst.getInput(dstParamId);
299       if (ref == null) {
300         ref = createRef(src.getOutputType(srcParamId));
301       }
302     }
303 
304     src.putOutput(srcParamId, ref);
305     dst.putInput(dstParamId, ref);
306 
307   }
308 
309   /**
310    * Binds a process input to another process input through a mapping.
311    * <p>
312    * This method is used to map child process inputs to parent process ones.
313    *
314    * @param src The source process
315    * @param dst The destination process
316    * @param srcParamId The source output
317    * @param dstParamId The destination input
318    */
319   protected final void bindInputInput(Process src, Process dst,
320     String srcParamId, String dstParamId) {
321 
322     LOG.debug("Binding inputs:");
323     LOG.debug("  - Source: " + src.getId() + "#" + srcParamId);
324     LOG.debug("  - Target: " + dst.getId() + "#" + dstParamId);
325 
326     // Map the target input parameter to the source one
327     dst.setAttribute(new QName("input-map", srcParamId), dstParamId);
328     Class<?> inputType = dst.getInputType(dstParamId);
329     dst.unuseInput(dstParamId);
330     dst.useInput(srcParamId, inputType);
331 
332   }
333 
334   /**
335    * Binds a process output to another process output through a mapping.
336    * <p>
337    * This method is used to map child process outputs to parent process ones.
338    *
339    * @param src The source process
340    * @param dst The destination process
341    * @param srcParamId The source output
342    * @param dstParamId The destination input
343    */
344   protected final void bindOutputOutput(Process src, Process dst,
345     String srcParamId, String dstParamId) {
346 
347     LOG.debug("Binding outputs:");
348     LOG.debug("  - Source: " + src.getId() + "#" + srcParamId);
349     LOG.debug("  - Target: " + dst.getId() + "#" + dstParamId);
350 
351     // Map the source output parameter to the target one
352     src.setAttribute(new QName("output-map", dstParamId), srcParamId);
353     Class<?> outputType = src.getOutputType(srcParamId);
354     src.unuseOutput(srcParamId);
355     src.useOutput(dstParamId, outputType);
356 
357   }
358 
359   /**
360    * Sets a process attribute within the "profile" namespace.
361    *
362    * @param p      A Process
363    * @param name   The attribute local name
364    * @param value  The attribute value
365    */
366   protected final void setProfileAttribute(Process p, String name, Object value) {
367     if (value != null) {
368       p.setAttribute(new QName(NS_PROFILE, name), value.toString());
369     }
370   }
371 }