Project

General

Profile

1
package eu.dnetlib.functionality.modular.ui.workflows.controllers;
2

    
3
import java.awt.image.BufferedImage;
4
import java.io.IOException;
5
import java.io.OutputStream;
6
import java.io.StringReader;
7
import java.util.Collections;
8
import java.util.Iterator;
9
import java.util.List;
10
import java.util.Map;
11
import java.util.Set;
12

    
13
import javax.annotation.Resource;
14
import javax.imageio.ImageIO;
15
import javax.servlet.http.HttpServletResponse;
16

    
17
import org.apache.commons.io.IOUtils;
18
import org.apache.commons.lang.math.NumberUtils;
19
import org.apache.commons.logging.Log;
20
import org.apache.commons.logging.LogFactory;
21
import org.dom4j.Document;
22
import org.dom4j.Element;
23
import org.dom4j.io.SAXReader;
24
import org.joda.time.DateTime;
25
import org.joda.time.format.DateTimeFormat;
26
import org.joda.time.format.DateTimeFormatter;
27
import org.springframework.stereotype.Controller;
28
import org.springframework.web.bind.annotation.RequestMapping;
29
import org.springframework.web.bind.annotation.RequestParam;
30
import org.springframework.web.bind.annotation.ResponseBody;
31

    
32
import com.google.common.base.Function;
33
import com.google.common.collect.Iterators;
34
import com.google.common.collect.Lists;
35
import com.google.common.collect.Maps;
36
import com.google.gson.Gson;
37
import com.googlecode.sarasvati.GraphProcess;
38
import com.googlecode.sarasvati.Node;
39
import com.googlecode.sarasvati.NodeToken;
40
import com.googlecode.sarasvati.ProcessState;
41

    
42
import eu.dnetlib.common.logging.DnetLogger;
43
import eu.dnetlib.common.logging.LogMessage;
44
import eu.dnetlib.enabling.is.lookup.rmi.ISLookUpException;
45
import eu.dnetlib.functionality.modular.ui.workflows.objects.AdvancedMetaWorkflowDescriptor;
46
import eu.dnetlib.functionality.modular.ui.workflows.objects.AtomicWorkflowDescriptor;
47
import eu.dnetlib.functionality.modular.ui.workflows.objects.MetaWorkflowDescriptor;
48
import eu.dnetlib.functionality.modular.ui.workflows.objects.NodeInfo;
49
import eu.dnetlib.functionality.modular.ui.workflows.objects.NodeTokenInfo;
50
import eu.dnetlib.functionality.modular.ui.workflows.objects.ProcessListEntry;
51
import eu.dnetlib.functionality.modular.ui.workflows.objects.sections.WorkflowSectionGrouper;
52
import eu.dnetlib.functionality.modular.ui.workflows.sarasvati.viewer.ProcessGraphGenerator;
53
import eu.dnetlib.functionality.modular.ui.workflows.util.ISLookupClient;
54
import eu.dnetlib.functionality.modular.ui.workflows.util.ISRegistryClient;
55
import eu.dnetlib.miscutils.datetime.DateUtils;
56
import eu.dnetlib.msro.workflows.sarasvati.loader.ProfileToSarasvatiConverter;
57
import eu.dnetlib.msro.workflows.sarasvati.loader.WorkflowExecutor;
58
import eu.dnetlib.msro.workflows.sarasvati.registry.GraphProcessRegistry;
59
import eu.dnetlib.msro.workflows.util.ProcessUtils;
60
import eu.dnetlib.msro.workflows.util.WorkflowsConstants;
61
import eu.dnetlib.msro.workflows.util.WorkflowsConstants.WorkflowStatus;
62

    
63
/**
64
 * Web controller for the UI
65
 * 
66
 * @author Michele Artini
67
 */
68

    
69
@Controller
70
public class WorkflowsController {
71

    
72
	private final class JournalEntryFunction implements Function<Map<String, String>, ProcessListEntry> {
73

    
74
		@Override
75
		public ProcessListEntry apply(final Map<String, String> input) {
76
			String name = input.get(WorkflowsConstants.SYSTEM_WF_PROFILE_NAME);
77
			if (input.containsKey(WorkflowsConstants.DATAPROVIDER_NAME)) {
78
				name += " (" + input.get(WorkflowsConstants.DATAPROVIDER_NAME) + ")";
79
			}
80
			final String procId = input.get(WorkflowsConstants.SYSTEM_WF_PROCESS_ID);
81
			final String wfId = input.get(WorkflowsConstants.SYSTEM_WF_PROFILE_ID);
82
			final String family = input.get(WorkflowsConstants.SYSTEM_WF_PROFILE_FAMILY);
83
			final long date = NumberUtils.toLong(input.get(LogMessage.LOG_DATE_FIELD), 0);
84
			final String status = Boolean.valueOf(input.get(WorkflowsConstants.SYSTEM_COMPLETED_SUCCESSFULLY)) ? "SUCCESS" : "FAILURE";
85

    
86
			return new ProcessListEntry(procId, wfId, name, family, status, date);
87
		}
88
	}
89

    
90
	@Resource
91
	private ISLookupClient isLookupClient;
92

    
93
	@Resource
94
	private ISRegistryClient isRegistryClient;
95

    
96
	@Resource
97
	private GraphProcessRegistry graphProcessRegistry;
98

    
99
	@Resource
100
	private ProcessGraphGenerator processGraphGenerator;
101

    
102
	@Resource
103
	private WorkflowSectionGrouper workflowSectionGrouper;
104

    
105
	@Resource
106
	private WorkflowExecutor workflowExecutor;
107

    
108
	@Resource
109
	private ProfileToSarasvatiConverter profileToSarasvatiConverter;
110

    
111
	@Resource(name = "msroWorkflowLogger")
112
	private DnetLogger dnetLogger;
113

    
114
	private static final Log log = LogFactory.getLog(WorkflowsController.class);
115

    
116
	@RequestMapping("/ui/list_metaworkflows.json")
117
	public @ResponseBody List<MetaWorkflowDescriptor> listMetaWorflowsForSection(@RequestParam(value = "section", required = false) final String sectionName, @RequestParam(value = "dsId", required = false) final String dsId)
118
			throws ISLookUpException, IOException {
119
		if (sectionName != null) {
120
			return workflowSectionGrouper.listMetaWorflowsForSection(sectionName);
121
		} else if (dsId != null) {
122
			return workflowSectionGrouper.listMetaWorflowsForDatasource(dsId);
123
		} else {
124
			return Lists.newArrayList();
125
		}
126
	}
127

    
128
	@RequestMapping("/ui/wf_metaworkflow.json")
129
	public @ResponseBody AdvancedMetaWorkflowDescriptor getMetaWorkflow(@RequestParam(value = "id", required = true) final String id) throws Exception {
130
		final AdvancedMetaWorkflowDescriptor wf = isLookupClient.getMetaWorkflow(id);
131
		final String xml = isLookupClient.getSarasvatiMetaWorkflow(id);
132
		
133
		wf.setMapContent(processGraphGenerator.getMetaWfDescImageMap(id, xml));
134

    
135
		return wf;
136
	}
137

    
138
	@RequestMapping("/ui/wf_atomic_workflow.json")
139
	public @ResponseBody AtomicWorkflowDescriptor getAtomicWorkflow(@RequestParam(value = "id", required = true) final String id) throws Exception {
140
		final AtomicWorkflowDescriptor wf = isLookupClient.getAtomicWorkflow(id);
141
		final String xml = profileToSarasvatiConverter.getSarasvatiWorkflow(id).getWorkflowXml();
142

    
143
		wf.setMapContent(processGraphGenerator.getWfDescImageMap(id, xml));
144

    
145
		return wf;
146
	}
147

    
148
	@RequestMapping("/ui/wf_atomic_workflow.img")
149
	public void showAtomicWorkflow(final HttpServletResponse response, @RequestParam(value = "id", required = true) final String id) throws Exception {
150

    
151
		String xml = profileToSarasvatiConverter.getSarasvatiWorkflow(id).getWorkflowXml();
152
		Set<String> notConfiguredNodes = isLookupClient.getNotConfiguredNodes(id);
153
		BufferedImage image = processGraphGenerator.getWfDescImage(id, xml, notConfiguredNodes);
154
		sendImage(response, image);
155
	}
156

    
157
	private void sendImage(final HttpServletResponse response, final BufferedImage image) throws IOException {
158
		response.setContentType("image/png");
159
		OutputStream out = response.getOutputStream();
160
		ImageIO.write(image, "png", out);
161
		out.flush();
162
		out.close();
163
	}
164

    
165
	@RequestMapping("/ui/wf.start")
166
	public void startWorkflow(final HttpServletResponse response, @RequestParam(value = "id", required = true) final String id) throws Exception {
167
		final String procId = workflowExecutor.startProcess(id);
168
		IOUtils.copy(new StringReader(new Gson().toJson(procId)), response.getOutputStream());
169
	}
170

    
171
	@RequestMapping("/ui/metawf.start")
172
	public void startMetaWorkflow(final HttpServletResponse response, @RequestParam(value = "id", required = true) final String id) throws Exception {
173
		workflowExecutor.startMetaWorkflow(id);
174
		IOUtils.copy(new StringReader(new Gson().toJson(id)), response.getOutputStream());
175
	}
176

    
177
	@RequestMapping("/ui/wf_workflow_node.json")
178
	public void workflowNode_info(final HttpServletResponse response,
179
			@RequestParam(value = "wf", required = true) final String wfId,
180
			@RequestParam(value = "node", required = true) final String nodeName) throws ISLookUpException, IOException {
181

    
182
		NodeInfo info = isLookupClient.getNodeInfo(wfId, nodeName);
183
		IOUtils.copy(new StringReader(new Gson().toJson(info)), response.getOutputStream());
184
	}
185

    
186
	@RequestMapping("/ui/wf_workflow_node.save")
187
	public void workflowNode_save(final HttpServletResponse response,
188
			@RequestParam(value = "wf", required = true) final String wfId,
189
			@RequestParam(value = "json", required = true) final String json) throws Exception {
190

    
191
		final Gson gson = new Gson();
192
		final NodeInfo info = gson.fromJson(json, NodeInfo.class);
193

    
194
		log.info("Updating node " + info.getName() + " of wf " + wfId);
195

    
196
		final String xml = isLookupClient.getProfile(wfId);
197
		boolean res = isRegistryClient.updateSarasvatiWorkflow(wfId, xml, info);
198

    
199
		for (String metaWfId : isLookupClient.listMetaWorflowsForWfId(wfId)) {
200
			if (isLookupClient.isExecutable(metaWfId)) {
201
				isRegistryClient.updateMetaWorkflowStatus(metaWfId, WorkflowStatus.EXECUTABLE);
202
			} else {
203
				isRegistryClient.updateMetaWorkflowStatus(metaWfId, WorkflowStatus.WAIT_USER_SETTINGS);
204
			}
205
		}
206

    
207
		IOUtils.copy(new StringReader(gson.toJson(res)), response.getOutputStream());
208
	}
209

    
210
	@RequestMapping("/ui/wf_metaworkflow.img")
211
	public void showMetaWorkflow(final HttpServletResponse response, @RequestParam(value = "id", required = true) final String id) throws Exception {
212

    
213
		String xml = isLookupClient.getSarasvatiMetaWorkflow(id);
214

    
215
		BufferedImage image = processGraphGenerator.getMetaWfDescImage(id, xml);
216
		sendImage(response, image);
217
	}
218

    
219
	@RequestMapping("/ui/wf_metaworkflow.edit")
220
	public void scheduleMetaWorkflow(final HttpServletResponse response, @RequestParam(value = "json", required = true) final String json) throws Exception {
221

    
222
		final Gson gson = new Gson();
223
		final AdvancedMetaWorkflowDescriptor info = gson.fromJson(json, AdvancedMetaWorkflowDescriptor.class);
224

    
225
		log.info("Updating workflow " + info.getName());
226

    
227
		final String xml = isLookupClient.getProfile(info.getWfId());
228
		boolean res = isRegistryClient.updateSarasvatiMetaWorkflow(info.getWfId(), xml, info);
229

    
230
		IOUtils.copy(new StringReader(gson.toJson(res)), response.getOutputStream());
231
	}
232

    
233
	@RequestMapping("/ui/wf_metaworkflow.clone")
234
	public void cloneMetaWf(final HttpServletResponse response,
235
			@RequestParam(value = "id", required = true) final String id,
236
			@RequestParam(value = "name", required = true) final String name) throws Exception {
237

    
238
		if (name.trim().length() > 0) {
239
			final Gson gson = new Gson();
240
			final String xml = isLookupClient.getProfile(id);
241
			SAXReader reader = new SAXReader();
242
			Document doc = reader.read(new StringReader(xml));
243
			doc.selectSingleNode("//METAWORKFLOW_NAME").setText(name);
244
			for (Object o : doc.selectNodes("//WORKFLOW")) {
245
				Element n = (Element) o;
246
				String atomWfXml = isLookupClient.getProfile(n.valueOf("@id"));
247
				String newAtomWfId = isRegistryClient.registerProfile(atomWfXml);
248
				n.addAttribute("id", newAtomWfId);
249
			}
250
			String res = isRegistryClient.registerProfile(doc.asXML());
251
			IOUtils.copy(new StringReader(gson.toJson(res)), response.getOutputStream());
252
		} else throw new IllegalArgumentException("Name is empty");
253
	}
254

    
255
	@RequestMapping("/ui/wf_proc.json")
256
	public void getProcessWorkflow(final HttpServletResponse response, @RequestParam(value = "id", required = true) final String id) throws Exception {
257
		final GraphProcess process = graphProcessRegistry.findProcess(id);
258

    
259
		final String mapContent = (process.getState() == ProcessState.Created) ? "" : processGraphGenerator.getProcessImageMap(id);
260

    
261
		String status = "";
262
		if (!process.isComplete()) {
263
			status = process.getState().toString().toUpperCase();
264
		} else if ("true".equals(process.getEnv().getAttribute(WorkflowsConstants.SYSTEM_COMPLETED_SUCCESSFULLY))) {
265
			status = "SUCCESS";
266
		} else {
267
			status = "FAILURE";
268
		}
269

    
270
		final String img = (process.getState() == ProcessState.Created) ? "../resources/img/notStarted.gif" : "wf_proc.img?id=" + id + "&t=" + DateUtils.now();
271
		final int priority = NumberUtils.toInt(process.getEnv().getAttribute(WorkflowsConstants.SYSTEM_WF_PRIORITY), WorkflowsConstants.DEFAULT_WF_PRIORITY);
272
		final String name = process.getGraph().getName();
273

    
274
		final AtomicWorkflowDescriptor wf = new AtomicWorkflowDescriptor(id, name, priority, status, mapContent, img, true, "auto", "RUNNING");
275

    
276
		IOUtils.copy(new StringReader(new Gson().toJson(wf)), response.getOutputStream());
277
	}
278

    
279
	@RequestMapping("/ui/wf_proc_node.json")
280
	public void getProcessWorkflowNode(final HttpServletResponse response,
281
			@RequestParam(value = "id", required = true) final String pid,
282
			@RequestParam(value = "node", required = true) final long nid) throws Exception {
283

    
284
		final NodeToken token = findNodeToken(pid, nid);
285

    
286
		final NodeTokenInfo info = (token == null) ? new NodeTokenInfo(findNodeName(pid, nid)) : new NodeTokenInfo(token);
287

    
288
		IOUtils.copy(new StringReader(new Gson().toJson(info)), response.getOutputStream());
289
	}
290

    
291
	private NodeToken findNodeToken(final String pid, final long nid) {
292
		final GraphProcess process = graphProcessRegistry.findProcess(pid);
293
		if (process != null) {
294
			for (NodeToken token : process.getNodeTokens()) {
295
				if (token.getNode().getId() == nid) return token;
296
			}
297
		}
298
		return null;
299
	}
300

    
301
	private String findNodeName(final String pid, final long nid) {
302
		final GraphProcess process = graphProcessRegistry.findProcess(pid);
303
		if (process != null) {
304
			for (Node node : process.getGraph().getNodes()) {
305
				if (node.getId() == nid) return node.getName();
306
			}
307
		}
308
		return "-";
309
	}
310

    
311
	@RequestMapping("/ui/wf_proc.img")
312
	public void showProcessWorkflow(final HttpServletResponse response, @RequestParam(value = "id", required = true) final String id) throws Exception {
313
		BufferedImage image = processGraphGenerator.getProcessImage(id);
314
		sendImage(response, image);
315
	}
316

    
317
	@RequestMapping("/ui/wf_proc.kill")
318
	public void killProcessWorkflow(final HttpServletResponse response, @RequestParam(value = "id", required = true) final String id) throws Exception {
319
		GraphProcess proc = graphProcessRegistry.findProcess(id);
320
		proc.setState(ProcessState.Canceled);
321
		getProcessWorkflow(response, id);
322
	}
323

    
324
	@RequestMapping("/ui/wf_journal.range")
325
	public @ResponseBody List<ProcessListEntry> rangeWfJournal(@RequestParam(value = "start", required = true) final String start,
326
			@RequestParam(value = "end", required = true) final String end) throws Exception {
327
		DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
328
		DateTime startDate = formatter.parseDateTime(start);
329
		// System.out.println(startDate.toString());
330
		DateTime endDate = formatter.parseDateTime(end).plusHours(23).plusMinutes(59).plusSeconds(59);
331
		// System.out.println(endDate.toString());
332
		final Iterator<Map<String, String>> iter = dnetLogger.range(startDate.toDate(), endDate.toDate());
333
		
334
		return Lists.newArrayList(Iterators.transform(iter, new JournalEntryFunction()));
335
		
336
	}
337
	
338
	@RequestMapping("/ui/recentWfs")
339
	public @ResponseBody List<ProcessListEntry> recentWfs(@RequestParam(value = "wfs", required = false) final String wfs) throws IOException {
340
		final List<ProcessListEntry> res = Lists.newArrayList();
341
				
342
		@SuppressWarnings("unchecked")
343
		final Set<String> wfFilter = (wfs != null && !wfs.isEmpty()) ? (new Gson()).fromJson(wfs, Set.class) : null;
344
				
345
		for (String pid : graphProcessRegistry.listIdentifiers()) {
346
			final GraphProcess proc = graphProcessRegistry.findProcess(pid);
347
			if (wfFilter == null || wfFilter.contains(ProcessUtils.calculateWfId(proc))) {
348
				res.add(new ProcessListEntry(pid, proc));
349
			}
350
		}
351
		return res;
352
	}
353

    
354
	@RequestMapping("/ui/wf_journal.find")
355
	public @ResponseBody List<ProcessListEntry> findWfJournalLogs(@RequestParam(value = "id", required = true) final String wfId) throws Exception {
356
		final Iterator<Map<String, String>> iter = dnetLogger.find("system:profileId", wfId);
357
		return Lists.newArrayList(Iterators.transform(iter, new JournalEntryFunction()));
358
	}
359

    
360
	@RequestMapping("/ui/wf_journal.get")
361
	public void getWfJournalLog(final HttpServletResponse response, @RequestParam(value = "id", required = true) final String id) throws Exception {
362
		final Map<String, String> map = dnetLogger.findOne("system:processId", id);
363

    
364
		final List<String> keys = Lists.newArrayList(map.keySet());
365
		Collections.sort(keys);
366

    
367
		final List<Map<String, String>> list = Lists.transform(keys, new Function<String, Map<String, String>>() {
368

    
369
			@Override
370
			public Map<String, String> apply(final String k) {
371
				final Map<String, String> res = Maps.newHashMap();
372
				res.put("k", k);
373
				res.put("v", map.get(k));
374
				return res;
375
			}
376
		});
377

    
378
		IOUtils.copy(new StringReader(new Gson().toJson(list)), response.getOutputStream());
379
	}
380

    
381
	@RequestMapping("/ui/wf_atomic_workflow.enable")
382
	public void enableAtomicWf(final HttpServletResponse response,
383
			@RequestParam(value = "id", required = true) final String id,
384
			@RequestParam(value = "start", required = true) final String value) throws Exception {
385

    
386
		isRegistryClient.configureWorkflowStart(id, value);
387

    
388
		IOUtils.copy(new StringReader(new Gson().toJson(value)), response.getOutputStream());
389
	}
390

    
391
	@RequestMapping("/ui/wf_atomic_workflow.priority")
392
	public void changeAtomicWfPriority(final HttpServletResponse response,
393
			@RequestParam(value = "id", required = true) final String id,
394
			@RequestParam(value = "value", required = true) final int value) throws Exception {
395
		isRegistryClient.changeAtomicWfPriority(id, value);
396

    
397
		IOUtils.copy(new StringReader(new Gson().toJson(value)), response.getOutputStream());
398
	}
399

    
400
}
(6-6/6)