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.io.StringWriter;
8
import java.util.Collection;
9
import java.util.Collections;
10
import java.util.Iterator;
11
import java.util.List;
12
import java.util.Map;
13
import java.util.Set;
14
import java.util.UUID;
15

    
16
import javax.annotation.Resource;
17
import javax.imageio.ImageIO;
18
import javax.servlet.http.HttpServletResponse;
19

    
20
import org.apache.commons.lang.math.NumberUtils;
21
import org.apache.commons.logging.Log;
22
import org.apache.commons.logging.LogFactory;
23
import org.dom4j.Document;
24
import org.dom4j.DocumentException;
25
import org.dom4j.Node;
26
import org.dom4j.io.SAXReader;
27
import org.joda.time.DateTime;
28
import org.joda.time.format.DateTimeFormat;
29
import org.joda.time.format.DateTimeFormatter;
30
import org.springframework.beans.factory.annotation.Autowired;
31
import org.springframework.stereotype.Controller;
32
import org.springframework.web.bind.annotation.RequestMapping;
33
import org.springframework.web.bind.annotation.RequestParam;
34
import org.springframework.web.bind.annotation.ResponseBody;
35

    
36
import com.google.common.base.Function;
37
import com.google.common.collect.Iterators;
38
import com.google.common.collect.Lists;
39
import com.google.common.collect.Maps;
40
import com.google.gson.Gson;
41
import com.google.gson.reflect.TypeToken;
42

    
43
import eu.dnetlib.common.logging.DnetLogger;
44
import eu.dnetlib.common.logging.LogMessage;
45
import eu.dnetlib.enabling.datastructures.MetaWorkflow;
46
import eu.dnetlib.enabling.datastructures.Workflow;
47
import eu.dnetlib.enabling.datastructures.WorkflowInstance;
48
import eu.dnetlib.enabling.datastructures.WorkflowInstance.StartMode;
49
import eu.dnetlib.enabling.is.client.InformationServiceClient;
50
import eu.dnetlib.functionality.modular.ui.AbstractAjaxController;
51
import eu.dnetlib.functionality.modular.ui.workflows.objects.NodeInfo;
52
import eu.dnetlib.functionality.modular.ui.workflows.sarasvati.viewer.ProcessGraphGenerator;
53
import eu.dnetlib.miscutils.DateUtils;
54
import eu.dnetlib.msro.dispatcher.ProcessInfo;
55
import eu.dnetlib.msro.dispatcher.WorkflowDispatcher;
56
import eu.dnetlib.msro.dispatcher.WorkflowRegistry;
57
import eu.dnetlib.msro.worker.WorkflowConstants;
58
import eu.dnetlib.rmi.objects.is.BlackboardActionStatus;
59
import eu.dnetlib.rmi.objects.is.DnetDataStructure;
60
import eu.dnetlib.rmi.soap.exceptions.InformationServiceException;
61

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

    
68
@Controller
69
public class WorkflowsController extends AbstractAjaxController {
70

    
71
	private final class JournalEntryFunction implements Function<Map<String, String>, ProcessInfo> {
72

    
73
		@Override
74
		public ProcessInfo apply(final Map<String, String> input) {
75
			final String name = input.get(WorkflowConstants.SYSTEM_WF_PROFILE_NAME);
76

    
77
			String repo = "";
78
			if (input.containsKey(WorkflowConstants.DATAPROVIDER_NAME)) {
79
				repo += input.get(WorkflowConstants.DATAPROVIDER_NAME);
80
			}
81
			final String procId = input.get(WorkflowConstants.SYSTEM_WF_PROCESS_ID);
82
			final String metaWfId = input.get(WorkflowConstants.SYSTEM_METAWF_ID);
83
			final String family = input.get(WorkflowConstants.SYSTEM_WF_PROFILE_FAMILY);
84
			final long date = NumberUtils.toLong(input.get(LogMessage.LOG_DATE_FIELD), 0);
85
			final BlackboardActionStatus status = Boolean.valueOf(input.get(WorkflowConstants.SYSTEM_COMPLETED_SUCCESSFULLY)) ? BlackboardActionStatus.DONE
86
					: BlackboardActionStatus.FAILED;
87

    
88
			final ProcessInfo info = new ProcessInfo(metaWfId, name, repo, family, procId);
89
			info.setLastActivityDate(date);
90
			info.setStatus(status);
91

    
92
			return info;
93
		}
94
	}
95

    
96
	@Autowired
97
	private WorkflowDispatcher dispatcher;
98

    
99
	@Autowired
100
	private WorkflowRegistry wfRegistry;
101

    
102
	@Autowired
103
	private InformationServiceClient isClient;
104

    
105
	@Resource
106
	private ProcessGraphGenerator processGraphGenerator;
107

    
108
	@Resource(name = "msroWorkflowLogger")
109
	private DnetLogger dnetLogger;
110

    
111
	private static final Log log = LogFactory.getLog(WorkflowsController.class);
112

    
113
	@RequestMapping("/ui/list_metaworkflows.json")
114
	public @ResponseBody
115
	List<MetaWorkflow> listMetaWorflowsForSection(@RequestParam(value = "section", required = false) final String sectionName,
116
			@RequestParam(value = "dsId", required = false) final String dsId) {
117

    
118
		if (sectionName != null || dsId != null) {
119
			final StringWriter sql = new StringWriter();
120
			sql.append("SELECT ds.content ");
121
			sql.append("FROM datastructures ds ");
122
			sql.append("LEFT OUTER JOIN resource_properties p ON (ds.id = p.resource) ");
123
			sql.append("WHERE ds.type = 'MetaWorkflow' ");
124
			sql.append("AND ");
125
			sql.append(sectionName != null ? "p.name='section' AND p.value='" + sectionName + "'" : "p.name='datasource' AND p.value='" + dsId + "'");
126

    
127
			try {
128
				return isClient.searchResourceByQuery(sql.toString(), MetaWorkflow.class);
129
			} catch (InformationServiceException e) {
130
				log.error("Error obtaining MetaWorkflows, using query: " + sql, e);
131
			}
132
		}
133
		return Lists.newArrayList();
134
	}
135

    
136
	@RequestMapping("/ui/wf_metaworkflow.json")
137
	public @ResponseBody
138
	MetaWorkflow getMetaWorkflow(@RequestParam(value = "id", required = true) final String id) throws Exception {
139
		return isClient.getResourceByCode(id, MetaWorkflow.class);
140
	}
141

    
142
	@RequestMapping("/ui/workflow.img")
143
	public void getWorkflowImage(final HttpServletResponse response, @RequestParam(value = "code", required = true) final String code) throws Exception {
144
		final DnetDataStructure ds = isClient.getDatastructureByCode(code, Workflow.class);
145
		final BufferedImage image = processGraphGenerator.getWfDescImage(code, ds.getName(), ds.getContent());
146
		sendImage(response, image);
147
	}
148

    
149
	@RequestMapping("/ui/workflow.map")
150
	public @ResponseBody
151
	String getWorkflowImageMap(@RequestParam(value = "code", required = true) final String code) throws Exception {
152
		final DnetDataStructure ds = isClient.getDatastructureByCode(code, Workflow.class);
153
		return processGraphGenerator.getWfDescImageMap(code, ds.getName(), ds.getContent());
154
	}
155

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

    
164
	@RequestMapping("/ui/wf.start")
165
	public @ResponseBody
166
	String startWorkflow(@RequestParam(value = "metaWf", required = true) final String metaWfId,
167
			@RequestParam(value = "wf", required = true) final String wfName) throws Exception {
168
		return dispatcher.dispatchWorkFlow(metaWfId, wfName);
169
	}
170

    
171
	@RequestMapping("/ui/metawf.start")
172
	public @ResponseBody
173
	String startMetaWorkflow(@RequestParam(value = "metaWf", required = true) final String metaWfId) throws Exception {
174
		return dispatcher.dispatchMetaWorkFlow(metaWfId);
175
	}
176

    
177
	@RequestMapping("/ui/wf_workflow_node.json")
178
	public @ResponseBody
179
	NodeInfo workflowNode_info(@RequestParam(value = "wf", required = true) final String wfCode,
180
			@RequestParam(value = "node", required = true) final String nodeName) throws InformationServiceException, DocumentException {
181
		final String xml = isClient.getXmlResourceByCode(wfCode, Workflow.class);
182

    
183
		final Document doc = new SAXReader().read(new StringReader(xml));
184
		final Node n = doc.selectSingleNode("//NODE[@name='" + nodeName + "']");
185
		final String name = nodeName;
186
		final String description = n.valueOf("./DESCRIPTION");
187
		final boolean start = "true".equalsIgnoreCase(n.valueOf("@isStart"));
188
		final boolean join = "true".equalsIgnoreCase(n.valueOf("@isJoin"));
189
		final Map<String, String> params = Maps.newHashMap();
190
		for (Object o : n.selectNodes("./PARAMETER/PARAM")) {
191
			final Node p = (Node) o;
192
			params.put(p.valueOf("@name"), p.getText());
193
		}
194
		return new NodeInfo(name, description, start, join, params);
195

    
196
	}
197

    
198
	@RequestMapping("/ui/wf_metaworkflow.edit")
199
	public @ResponseBody
200
	boolean editMetaWd(@RequestParam(value = "json", required = true) final String json) throws Exception {
201
		final MetaWorkflow metaWf = new Gson().fromJson(json, MetaWorkflow.class);
202
		log.info("Updating metaWorkflow " + metaWf.getName());
203
		isClient.updateResource(metaWf);
204
		return true;
205
	}
206

    
207
	@RequestMapping("/ui/clone_metaworkflow.do")
208
	public @ResponseBody
209
	String cloneMetaWf(@RequestParam(value = "code", required = true) final String code,
210
			@RequestParam(value = "name", required = true) final String newName) throws Exception {
211

    
212
		if (newName.trim().length() > 0) {
213
			final String newCode = "metawf-" + UUID.randomUUID();
214
			final MetaWorkflow metaWf = isClient.getResourceByCode(code, MetaWorkflow.class);
215
			metaWf.setCode(newCode);
216
			metaWf.setName(newName);
217
			isClient.registerResource(metaWf);
218
			return newCode;
219
		} else {
220
			throw new IllegalArgumentException("Name is empty");
221
		}
222
	}
223

    
224
	/*
225
	 * @RequestMapping("/ui/wf_proc_node.json") public @ResponseBody NodeTokenInfo getProcessWorkflowNode(@RequestParam(value = "id",
226
	 * required = true) final String pid,
227
	 * 
228
	 * @RequestParam(value = "node", required = true) final long nid) throws Exception {
229
	 * 
230
	 * final NodeToken token = findNodeToken(pid, nid);
231
	 * 
232
	 * final NodeTokenInfo info = token == null ? new NodeTokenInfo(findNodeName(pid, nid)) : new NodeTokenInfo(token);
233
	 * 
234
	 * return info; }
235
	 */
236
	/*
237
	 * private NodeToken findNodeToken(final String pid, final long nid) { final GraphProcess process =
238
	 * graphProcessRegistry.findProcess(pid); if (process != null) { for (NodeToken token : process.getNodeTokens()) { if
239
	 * (token.getNode().getId() == nid) { return token; } } } return null; }
240
	 * 
241
	 * private String findNodeName(final String pid, final long nid) { final GraphProcess process = graphProcessRegistry.findProcess(pid);
242
	 * if (process != null) { for (Node node : process.getGraph().getNodes()) { if (node.getId() == nid) { return node.getName(); } } }
243
	 * return "-"; }
244
	 */
245
	@RequestMapping("/ui/wf_proc.img")
246
	public void showProcessWorkflow(final HttpServletResponse response, @RequestParam(value = "id", required = true) final String id) throws Exception {
247
		BufferedImage image = processGraphGenerator.getProcessImage(id);
248
		sendImage(response, image);
249
	}
250

    
251
	@RequestMapping("/ui/wf_proc.kill")
252
	public @ResponseBody
253
	boolean killProcessWorkflow(@RequestParam(value = "id", required = true) final String id) throws Exception {
254
		/*
255
		 * GraphProcess proc = graphProcessRegistry.findProcess(id); proc.setState(ProcessState.Canceled); return true;
256
		 */
257
		// TODO
258
		return false;
259
	}
260

    
261
	@RequestMapping("/ui/wf_journal.range")
262
	public @ResponseBody
263
	Collection<ProcessInfo> rangeWfJournal(@RequestParam(value = "start", required = true) final String start,
264
			@RequestParam(value = "end", required = true) final String end) throws Exception {
265

    
266
		final Map<String, ProcessInfo> res = Maps.newHashMap();
267

    
268
		final DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
269
		final DateTime startDate = formatter.parseDateTime(start);
270
		final DateTime endDate = formatter.parseDateTime(end).plusHours(23).plusMinutes(59).plusSeconds(59);
271

    
272
		final Iterator<ProcessInfo> iter = Iterators.transform(dnetLogger.range(startDate.toDate(), endDate.toDate()), new JournalEntryFunction());
273
		while (iter.hasNext()) {
274
			ProcessInfo e = iter.next();
275
			res.put(e.getProcId(), e);
276
		}
277

    
278
		long now = DateUtils.now();
279
		if (startDate.isBefore(now) && endDate.isAfter(now)) {
280
			for (String pid : wfRegistry.listProcIds()) {
281
				res.put(pid, wfRegistry.findProcess(pid));
282
			}
283
		}
284

    
285
		return res.values();
286

    
287
	}
288

    
289
	@RequestMapping("/ui/wf_journal.find")
290
	public @ResponseBody
291
	Collection<ProcessInfo> findWfJournal(@RequestParam(value = "wfs", required = true) final String wfs) {
292
		final Map<String, ProcessInfo> res = Maps.newHashMap();
293

    
294
		final Set<String> wfFilter = new Gson().fromJson(wfs, new TypeToken<Set<String>>() {}.getType());
295

    
296
		for (String wfId : wfFilter) {
297
			final Iterator<ProcessInfo> iter = Iterators.transform(dnetLogger.find(WorkflowConstants.SYSTEM_METAWF_ID, wfId),
298
					new JournalEntryFunction());
299
			while (iter.hasNext()) {
300
				ProcessInfo e = iter.next();
301
				res.put(e.getProcId(), e);
302
			}
303
		}
304

    
305
		for (String pid : wfRegistry.listProcIds()) {
306
			final ProcessInfo proc = wfRegistry.findProcess(pid);
307
			if (wfFilter.contains(proc.getMetaWfId())) {
308
				res.put(pid, proc);
309
			}
310
		}
311

    
312
		return res.values();
313
	}
314

    
315
	@RequestMapping("/ui/wf_journal_byFamily.find")
316
	public @ResponseBody
317
	Collection<ProcessInfo> findWfJournalByFamily(@RequestParam(value = "family", required = true) final String family) throws IOException {
318
		final Iterator<ProcessInfo> iter = Iterators.transform(dnetLogger.find(WorkflowConstants.SYSTEM_WF_PROFILE_FAMILY, family),
319
				new JournalEntryFunction());
320
		return Lists.newArrayList(iter);
321
	}
322

    
323
	@RequestMapping("/ui/wf_journal.get")
324
	public @ResponseBody
325
	Map<String, Object> getWfJournalLog(@RequestParam(value = "id", required = true) final String id) throws Exception {
326
		final Map<String, Object> res = Maps.newHashMap();
327

    
328
		final Map<String, String> logs = dnetLogger.findOne("system:processId", id);
329

    
330
		if (logs != null && !logs.isEmpty()) {
331
			final List<String> keys = Lists.newArrayList(logs.keySet());
332
			Collections.sort(keys);
333

    
334
			final List<Map<String, String>> journalEntry = Lists.newArrayList();
335
			for (String k : keys) {
336
				final Map<String, String> m = Maps.newHashMap();
337
				m.put("name", k);
338
				m.put("value", logs.get(k));
339
				journalEntry.add(m);
340
			}
341
			res.put("journal", journalEntry);
342
		}
343

    
344
		final ProcessInfo process = wfRegistry.findProcess(id);
345

    
346
		if (process != null) {
347
			/*
348
			 * final String mapContent = process.getState() == ProcessState.Created ? "" : processGraphGenerator.getProcessImageMap(id);
349
			 * 
350
			 * String status = ""; if (!process.isComplete()) { status = process.getState().toString().toUpperCase(); } else if
351
			 * ("true".equals(process.getEnv().getAttribute(WorkflowConstants.SYSTEM_COMPLETED_SUCCESSFULLY))) { status = "SUCCESS"; } else
352
			 * { status = "FAILURE"; }
353
			 * 
354
			 * final String img = process.getState() == ProcessState.Created ? "../resources/img/notStarted.gif" : "wf_proc.img?id=" + id +
355
			 * "&t=" + DateUtils.now();
356
			 * 
357
			 * final String name = process.getGraph().getName();
358
			 * 
359
			 * final long startDate = NumberUtils.toLong(process.getEnv().getAttribute(WorkflowConstants.SYSTEM_START_DATE), 0); final long
360
			 * endDate = NumberUtils.toLong(process.getEnv().getAttribute(WorkflowConstants.SYSTEM_END_DATE), 0);
361
			 * 
362
			 * final AtomicWorkflowDescriptor wf = new AtomicWorkflowDescriptor(id, name, status, mapContent, img, true, "auto", "RUNNING",
363
			 * startDate, endDate);
364
			 * 
365
			 * res.put("graph", wf);
366
			 */
367
			// TODO
368
		}
369

    
370
		return res;
371
	}
372

    
373
	@RequestMapping("/ui/wf_atomic_workflow.enable")
374
	public @ResponseBody
375
	String enableAtomicWf(@RequestParam(value = "metaWf", required = true) final String metaWfId,
376
			@RequestParam(value = "wf", required = true) final String wfName,
377
			@RequestParam(value = "start", required = true) final String value) throws Exception {
378

    
379
		final MetaWorkflow metaWf = isClient.getResourceByCode(metaWfId, MetaWorkflow.class);
380
		final WorkflowInstance wfi = metaWf.findWorkflowInstanceByName(wfName);
381
		if (wfi != null) {
382
			wfi.setStartMode(StartMode.valueOf(value.toUpperCase()));
383
			isClient.updateResource(metaWf);
384
		}
385

    
386
		return value;
387
	}
388
	/*
389
	 * @RequestMapping("/ui/workflow_user_params.json") public @ResponseBody List<NodeWithUserParams>
390
	 * listWorkflowUserParams(@RequestParam(value = "wf", required = true) final String wfId) throws Exception { return
391
	 * isLookupClient.listWorkflowUserParams(wfId); }
392
	 * 
393
	 * @RequestMapping(value = "/ui/save_user_params.do") public @ResponseBody boolean saveWorkflowUserParams(@RequestParam(value = "wf",
394
	 * required = true) final String wfId,
395
	 * 
396
	 * @RequestParam(value = "params", required = true) final String jsonParams) throws Exception {
397
	 * 
398
	 * final String xml = isLookupClient.getProfile(wfId);
399
	 * 
400
	 * final List<NodeWithUserParams> params = new Gson().fromJson(jsonParams, new TypeToken<List<NodeWithUserParams>>() {}.getType());
401
	 * 
402
	 * boolean res = isRegistryClient.updateSarasvatiWorkflow(wfId, xml, params);
403
	 * 
404
	 * for (String metaWfId : isLookupClient.listMetaWorflowsForWfId(wfId)) { if (isLookupClient.isExecutable(metaWfId)) {
405
	 * isRegistryClient.updateMetaWorkflowStatus(metaWfId, WorkflowStatus.EXECUTABLE); } else {
406
	 * isRegistryClient.updateMetaWorkflowStatus(metaWfId, WorkflowStatus.WAIT_USER_SETTINGS); } }
407
	 * 
408
	 * return res; }
409
	 */
410
}
(5-5/5)