Project

General

Profile

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

    
3
import java.io.IOException;
4
import java.io.StringReader;
5
import java.time.LocalDate;
6
import java.time.LocalDateTime;
7
import java.time.format.DateTimeFormatter;
8
import java.util.*;
9
import java.util.stream.Collectors;
10
import java.util.stream.StreamSupport;
11
import javax.annotation.Resource;
12
import javax.servlet.http.HttpServletResponse;
13

    
14
import com.google.common.base.Splitter;
15
import com.google.common.collect.Maps;
16
import com.google.common.collect.Sets;
17
import com.google.gson.Gson;
18
import com.google.gson.reflect.TypeToken;
19
import eu.dnetlib.enabling.locators.UniqueServiceLocator;
20
import eu.dnetlib.functionality.modular.ui.AbstractAjaxController;
21
import eu.dnetlib.functionality.modular.ui.repositories.objects.RepoHIWorkflow;
22
import eu.dnetlib.functionality.modular.ui.repositories.objects.VocabularyEntry;
23
import eu.dnetlib.functionality.modular.ui.repositories.util.RepoUIUtils;
24
import eu.dnetlib.functionality.modular.ui.workflows.objects.*;
25
import eu.dnetlib.functionality.modular.ui.workflows.objects.sections.WorkflowSectionGrouper;
26
import eu.dnetlib.functionality.modular.ui.workflows.util.ISLookupClient;
27
import eu.dnetlib.functionality.modular.ui.workflows.util.ISRegistryClient;
28
import eu.dnetlib.miscutils.functional.xml.ApplyXsltDom4j;
29
import eu.dnetlib.msro.logging.DnetLogger;
30
import eu.dnetlib.msro.logging.LogMessage;
31
import eu.dnetlib.msro.notification.NotificationCondition;
32
import eu.dnetlib.msro.workflows.graph.Graph;
33
import eu.dnetlib.msro.workflows.graph.GraphLoader;
34
import eu.dnetlib.msro.workflows.procs.ProcessRegistry;
35
import eu.dnetlib.msro.workflows.procs.WorkflowExecutor;
36
import eu.dnetlib.msro.workflows.procs.WorkflowProcess;
37
import eu.dnetlib.msro.workflows.procs.WorkflowProcess.StartMode;
38
import eu.dnetlib.msro.workflows.util.ProcessCallback;
39
import eu.dnetlib.msro.workflows.util.WorkflowsConstants;
40
import eu.dnetlib.rmi.data.ProtocolParameterType;
41
import eu.dnetlib.rmi.datasource.DatasourceManagerService;
42
import eu.dnetlib.rmi.datasource.DatasourceManagerServiceException;
43
import eu.dnetlib.rmi.enabling.ISLookUpException;
44
import eu.dnetlib.rmi.enabling.ISRegistryException;
45
import eu.dnetlib.rmi.manager.MSROException;
46
import org.apache.commons.io.IOUtils;
47
import org.apache.commons.lang3.StringUtils;
48
import org.apache.commons.lang3.math.NumberUtils;
49
import org.apache.commons.logging.Log;
50
import org.apache.commons.logging.LogFactory;
51
import org.dom4j.Document;
52
import org.dom4j.Element;
53
import org.dom4j.Node;
54
import org.dom4j.io.SAXReader;
55
import org.springframework.beans.factory.annotation.Autowired;
56
import org.springframework.beans.factory.annotation.Value;
57
import org.springframework.core.io.ClassPathResource;
58
import org.springframework.stereotype.Controller;
59
import org.springframework.web.bind.annotation.RequestMapping;
60
import org.springframework.web.bind.annotation.RequestParam;
61
import org.springframework.web.bind.annotation.ResponseBody;
62

    
63
// import com.google.common.collect.Lists;
64

    
65
/**
66
 * Web controller for the UI
67
 *
68
 * @author Michele Artini
69
 */
70

    
71
@Controller
72
public class WorkflowsController extends AbstractAjaxController {
73

    
74
	private static final Log log = LogFactory.getLog(WorkflowsController.class);
75

    
76
	@Autowired
77
	private ISLookupClient isLookupClient;
78

    
79
	@Autowired
80
	private ISRegistryClient isRegistryClient;
81

    
82
	@Autowired
83
	private RepoUIUtils repoUIUtils;
84

    
85
	@Autowired
86
	private GraphLoader graphLoader;
87

    
88
	@Autowired
89
	private ProcessRegistry graphProcessRegistry;
90

    
91
	@Autowired
92
	private WorkflowSectionGrouper workflowSectionGrouper;
93

    
94
	@Autowired
95
	private WorkflowExecutor workflowExecutor;
96

    
97
	@Resource(name = "msroWorkflowLogger")
98
	private DnetLogger dnetLogger;
99

    
100
	@Autowired
101
	private UniqueServiceLocator serviceLocator;
102

    
103
	@Value("${repo.ui.compatibilityLevels.vocabulary}")
104
	private String compatibilityLevelsVocabulary;
105

    
106
	@RequestMapping("/ui/wf/list_workflows.json")
107
	public @ResponseBody List<WorkflowItem> listWorflowsForSection(@RequestParam(value = "section", required = false) final String sectionName,
108
			@RequestParam(value = "dsId", required = false) final String dsId)
109
			throws ISLookUpException, IOException {
110
		if (sectionName != null) {
111
			return this.workflowSectionGrouper.listWorflowsForSection(sectionName);
112
		} else if (dsId != null) {
113
			return this.workflowSectionGrouper.listWorflowsForDatasource(dsId);
114
		} else {
115
			return new ArrayList<>();
116
		}
117
	}
118

    
119
	@RequestMapping("/ui/wf/workflow.html")
120
	public void getWorkflowHtml(final HttpServletResponse res, @RequestParam(value = "id", required = true) final String id) throws Exception {
121
		final SAXReader reader = new SAXReader();
122
		final String profile = this.isLookupClient.getProfile(id);
123
		final Document doc = reader.read(new StringReader(profile));
124
		final Element dsNode = (Element) doc.selectSingleNode("//DATASOURCE");
125
		if (dsNode != null) {
126
			final String dsId = dsNode.valueOf("@id");
127
			final String ifaceId = dsNode.valueOf("@interface");
128
			final String dsProfile = this.isLookupClient.getProfile(dsId);
129
			final Document doc2 = reader.read(new StringReader(dsProfile));
130
			dsNode.addAttribute("name", doc2.valueOf("//OFFICIAL_NAME"));
131
			dsNode.addAttribute("protocol", doc2.valueOf("//INTERFACE[@id = '" + ifaceId + "']/ACCESS_PROTOCOL"));
132
			final Node ifcNode = doc2.selectSingleNode("//INTERFACE[@id = '" + ifaceId + "']");
133
			final Element extraFields = dsNode.addElement("extraFields");
134
			for (final Object o : ifcNode.selectNodes("./INTERFACE_EXTRA_FIELD")) {
135
				final Element f = extraFields.addElement("field");
136
				f.addAttribute("name", ((Node) o).valueOf("@name"));
137
				f.setText(((Node) o).getText());
138
			}
139
		}
140

    
141
		final ApplyXsltDom4j xslt = new ApplyXsltDom4j(new ClassPathResource("/eu/dnetlib/functionality/modular/ui/workflows/xslt/wf_profile2html.xslt"));
142

    
143
		res.setContentType("text/html");
144
		IOUtils.copy(new StringReader(xslt.apply(doc)), res.getOutputStream());
145
	}
146

    
147
	@RequestMapping("/ui/wf/getGraph.do")
148
	public @ResponseBody GraphDetails getWorkflowGraphHtml(@RequestParam(value = "wfId", required = true) final String id) throws Exception {
149
		final String profile = this.isLookupClient.getProfile(id);
150
		final Document doc = new SAXReader().read(new StringReader(profile));
151

    
152
		final Graph graph = this.graphLoader.loadGraph(doc, new HashMap<>());
153

    
154
		return GraphDetails.from(graph);
155
	}
156

    
157
	@RequestMapping("/ui/wf/formProtocolParameters.find")
158
	public @ResponseBody List<Map<String, String>> getFormProtocolParameters(@RequestParam(value = "ds", required = true) final String repoId,
159
			@RequestParam(value = "iface", required = true) final String ifaceId)
160
			throws Exception {
161

    
162
		final String profile = this.isLookupClient.getRepoProfile(repoId);
163

    
164
		final SAXReader reader = new SAXReader();
165
		final Document doc = reader.read(new StringReader(profile));
166

    
167
		final Node ifcNode = doc.selectSingleNode("//INTERFACE[@id = '" + ifaceId + "']");
168
		final String protocol = ifcNode.valueOf("./ACCESS_PROTOCOL");
169

    
170
		final List<Map<String, String>> list = new ArrayList<>();
171
		final Map<String, String> baseUrlParam = new HashMap<>();
172

    
173
		baseUrlParam.put("name", "baseUrl");
174
		baseUrlParam.put("type", ProtocolParameterType.TEXT.toString());
175
		baseUrlParam.put("value", ifcNode.valueOf("./BASE_URL"));
176
		baseUrlParam.put("regex", "^(http|https|ftp|ftps|sftp|file):\\/\\/");
177
		list.add(baseUrlParam);
178

    
179
		this.repoUIUtils.listParametersForProtocol(protocol).stream().map(pp -> {
180
			final Map<String, String> res = new HashMap<>();
181
			res.put("name", pp.getName());
182
			res.put("type", pp.getType().toString());
183
			res.put("value", ifcNode.valueOf("./ACCESS_PROTOCOL/@" + pp.getName()));
184

    
185
			if (StringUtils.isNotBlank(pp.getRegex())) {
186
				res.put("regex", pp.getRegex());
187
			}
188
			if (pp.isFunctionPopulated()) {
189
				res.put("functionPopulated", "true");
190
			}
191
			if (pp.isOptional()) {
192
				res.put("optional", "true");
193
			}
194

    
195
			return res;
196
		}).forEach(list::add);;
197

    
198
		return list;
199
	}
200

    
201
	@RequestMapping("/ui/wf/repoApi.html")
202
	public void getWorkflowHtml(final HttpServletResponse res,
203
			@RequestParam(value = "repoId", required = true) final String repoId,
204
			@RequestParam(value = "ifaceId", required = true) final String ifaceId) throws Exception {
205

    
206
		final String profile = this.isLookupClient.getRepoProfile(repoId);
207

    
208
		final SAXReader reader = new SAXReader();
209
		final Document doc = reader.read(new StringReader(profile));
210

    
211
		final Node ifcNode = doc.selectSingleNode("//INTERFACE[@id = '" + ifaceId + "']");
212
		final String protocol = ifcNode.valueOf("./ACCESS_PROTOCOL");
213
		final Element extra = doc.getRootElement().addElement("extra_info");
214

    
215
		final Element compLevels = extra.addElement("compatibilityLevels");
216
		for (final VocabularyEntry e : this.repoUIUtils.fetchVocabularyTerms(this.compatibilityLevelsVocabulary)) {
217
			final Element l = compLevels.addElement("level");
218
			l.setText(e.getName());
219
		}
220

    
221
		final Element parameters = extra.addElement("parameters");
222
		for (final Object o : ifcNode.selectNodes("./ACCESS_PROTOCOL/@*")) {
223
			final Element p = parameters.addElement("param");
224
			p.addAttribute("name", ((Node) o).getName());
225
			p.setText(((Node) o).getText());
226
		}
227

    
228
		final Element extraFields = extra.addElement("extraFields");
229
		for (final Object o : ifcNode.selectNodes("./INTERFACE_EXTRA_FIELD")) {
230
			final Element f = extraFields.addElement("field");
231
			f.addAttribute("name", ((Node) o).valueOf("@name"));
232
			f.setText(((Node) o).getText());
233
		}
234

    
235
		final Element wfs = extra.addElement("workflows");
236
		for (final WorkflowItem item : this.isLookupClient.listWorflowsForApi(repoId, ifaceId)) {
237
			final Element wf = wfs.addElement("workflow");
238
			wf.addAttribute("id", item.getWfId());
239
			wf.addAttribute("name", item.getName());
240
			wf.addAttribute("description", item.getDesc());
241
			if (item.isDestroy()) {
242
				wf.addAttribute("destroy", "1");
243
			}
244
		}
245
		final Map<String, String> params = new HashMap<String, String>();
246
		params.put("profileId", doc.valueOf("//RESOURCE_IDENTIFIER/@value"));
247
		params.put("ifaceId", ifaceId);
248
		params.put("protocol", protocol);
249
		params.put("baseUrl", ifcNode.valueOf("./BASE_URL"));
250
		params.put("prefix", doc.valueOf(".//FIELD[./key = 'NamespacePrefix']/value"));
251
		params.put("typology", ifcNode.valueOf("@typology"));
252
		params.put("compliance", ifcNode.valueOf("@compliance"));
253
		params.put("overrideCompliance", ifcNode.valueOf("./INTERFACE_EXTRA_FIELD[@name='overriding_compliance']"));
254

    
255
		doc.selectSingleNode("/RESOURCE_PROFILE/HEADER").detach();
256
		doc.selectSingleNode("/RESOURCE_PROFILE/BODY/CONFIGURATION/INTERFACES").detach();
257
		doc.selectSingleNode("/RESOURCE_PROFILE/BODY/CONFIGURATION/EXTRA_FIELDS").detach();
258
		doc.selectSingleNode("/RESOURCE_PROFILE/BODY/QOS").detach();
259
		doc.selectSingleNode("/RESOURCE_PROFILE/BODY/STATUS").detach();
260
		doc.selectSingleNode("/RESOURCE_PROFILE/BODY/SECURITY_PARAMETERS").detach();
261
		doc.selectSingleNode("/RESOURCE_PROFILE/BODY/BLACKBOARD").detach();
262

    
263
		final ApplyXsltDom4j xslt = new ApplyXsltDom4j(new ClassPathResource("/eu/dnetlib/functionality/modular/ui/workflows/xslt/repoApi.xslt"), params);
264

    
265
		res.setContentType("text/html");
266

    
267
		IOUtils.copy(new StringReader(xslt.apply(doc)), res.getOutputStream());
268
	}
269

    
270
	@RequestMapping("/ui/wf/wf.start")
271
	public @ResponseBody ProcessInfo startWorkflow(@RequestParam(value = "id", required = true) final String id) throws Exception {
272
		final String procId = this.workflowExecutor.startWorkflow(id, null);
273
		final WorkflowProcess process = this.graphProcessRegistry.findProcess(procId);
274
		return process != null ? new ProcessInfo(process) : new ProcessInfo(procId);
275
	}
276

    
277
	@RequestMapping("/ui/wf/wfTemplate.start")
278
	public @ResponseBody ProcessInfo startWorkflowTemplate(
279
			@RequestParam(value = "node", required = true) final String nodeName,
280
			@RequestParam(value = "parentWf", required = true) final String parentWf) throws Exception {
281

    
282
		final String profile = this.isLookupClient.getProfile(parentWf);
283
		final Document doc = (new SAXReader()).read(new StringReader(profile));
284
		final String family = doc.valueOf("//WORKFLOW_FAMILY");
285
		final int priority = NumberUtils.toInt(doc.valueOf("//WORKFLOW_PRIORITY"), WorkflowsConstants.DEFAULT_WF_PRIORITY);
286
		final String dsId = doc.valueOf("//DATASOURCE/@id");
287
		final String iface = doc.valueOf("//DATASOURCE/@interface");
288

    
289
		final Map<String, String> globalParams = new HashMap<String, String>();
290
		for (final Object o : doc.selectNodes("//CONFIGURATION/PARAMETERS/PARAM")) {
291
			final Element p = (Element) o;
292
			globalParams.put(p.valueOf("@name"), p.getTextTrim());
293
		}
294

    
295
		final Map<String, Object> params =
296
				this.graphLoader.calculateParamsForNode(doc.selectSingleNode("//NODE[@name='" + nodeName + "']"), globalParams);
297

    
298
		if (!params.containsKey("wfTemplateId") || !(params.get("wfTemplateId") instanceof String)
299
				|| StringUtils.isBlank((String) params.get("wfTemplateId"))) {
300
			log.error("wfTemplateId is invalid or missing in profile " + parentWf);
301
			throw new MSROException("wfTemplateId is invalid or missing in profile " + parentWf);
302
		}
303

    
304
		if (params.containsKey("wfTemplateParams") && !(params.get("wfTemplateParams") instanceof Map)) {
305
			log.error("wfTemplateParams is invalid in profile " + parentWf);
306
			throw new MSROException("wfTemplateParams is invalid in profile " + parentWf);
307
		}
308

    
309
		final String wfTtemplateId = (String) params.get("wfTemplateId");
310
		@SuppressWarnings("unchecked")
311
		final Map<String, String> wfTtemplateParams =
312
				params.containsKey("wfTemplateParams") ? (Map<String, String>) params.get("wfTemplateParams") : new HashMap<>();
313

    
314
		final String procId = this.workflowExecutor.startWorkflowTemplate(wfTtemplateId, nodeName, family, priority, dsId, iface, wfTtemplateParams, null);
315

    
316
		final WorkflowProcess process = this.graphProcessRegistry.findProcess(procId);
317

    
318
		return process != null ? new ProcessInfo(process) : new ProcessInfo(procId);
319
	}
320

    
321
	@RequestMapping("/ui/wf/update_workflow.get")
322
	public @ResponseBody WorkflowUpdateInfo getWorkflowUpdateInfo(@RequestParam(value = "wfId", required = true) final String wfId) throws Exception {
323

    
324
		final String xml = this.isLookupClient.getProfile(wfId);
325

    
326
		final Document doc = (new SAXReader()).read(new StringReader(xml));
327

    
328
		final WorkflowUpdateInfo info = new WorkflowUpdateInfo();
329
		info.setWfId(wfId);
330
		info.setMode(StartMode.valueOf(doc.valueOf("//CONFIGURATION/@start")));
331
		info.setPriority(NumberUtils.toInt(doc.valueOf("//WORKFLOW_PRIORITY"), WorkflowsConstants.DEFAULT_WF_PRIORITY));
332

    
333
		info.setScheduled("true".equalsIgnoreCase(doc.valueOf("//SCHEDULING/@enabled")));
334
		info.setCron(doc.valueOf("//SCHEDULING/CRON"));
335
		info.setInterval(NumberUtils.toInt(doc.valueOf("//SCHEDULING/MININTERVAL"), 120)); // Default: 120 minutes
336

    
337
		for (final Object o : doc.selectNodes("//NOTIFICATIONS/EMAIL")) {
338
			info.getNotifications().add(new WorkflowNotificationInfo(
339
					((Element) o).valueOf("@address"),
340
					((Element) o).valueOf("@messageProfileId"),
341
					NotificationCondition.valueOf(((Element) o).valueOf("@condition"))));
342
		}
343

    
344
		return info;
345
	}
346

    
347
	@RequestMapping("/ui/wf/update_workflow.do")
348
	public @ResponseBody boolean updateWorkflow(@RequestParam(value = "json", required = true) final String json) throws Exception {
349

    
350
		final WorkflowUpdateInfo info = (new Gson()).fromJson(json, WorkflowUpdateInfo.class);
351

    
352
		log.info("Updating workflow " + info.getWfId());
353

    
354
		final String xml = this.isLookupClient.getProfile(info.getWfId());
355
		final boolean res = this.isRegistryClient.updateWorkflowProfile(info.getWfId(), xml, info);
356

    
357
		return res;
358
	}
359

    
360
	@RequestMapping("/ui/wf/proc.kill")
361
	public @ResponseBody boolean killProcessWorkflow(@RequestParam(value = "id", required = true) final String id) throws Exception {
362
		this.graphProcessRegistry.findProcess(id).kill();
363
		return true;
364
	}
365

    
366
	@RequestMapping("/ui/wf/journal.range")
367
	public @ResponseBody Collection<ProcessInfo> rangeWfJournal(@RequestParam(value = "start", required = true) final String start,
368
			@RequestParam(value = "end", required = true) final String end) throws Exception {
369

    
370
		final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
371
		final LocalDateTime startDate = LocalDate.parse(start, formatter).atTime(0, 0);
372
		final LocalDateTime endDate = LocalDate.parse(end, formatter).atTime(23, 59);
373

    
374
		final Iterator<Map<String, String>> iterator = this.dnetLogger.range(startDate, endDate);
375
		final LocalDateTime now = LocalDateTime.now();
376

    
377
		final Map<String, ProcessInfo> res = StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
378
				.map(this::toProcessInfo)
379
				.collect(Collectors.toMap(e -> e.getProcId(), e -> e, (e1, e2) -> e1));
380

    
381
		if (startDate.isBefore(now) && endDate.isAfter(now)) {
382
			for (final WorkflowProcess proc : this.graphProcessRegistry.listProcesses()) {
383
				res.put(proc.getId(), new ProcessInfo(proc));
384
			}
385
		}
386

    
387
		return res.values();
388
	}
389

    
390
	@RequestMapping("/ui/wf/journal.find")
391
	public @ResponseBody List<ProcessInfo> findWfJournal(@RequestParam(value = "wfs", required = true) final String wfs) {
392
		final Map<String, ProcessInfo> map = Maps.newHashMap();
393

    
394
		final Set<String> wfFilter = Sets.newHashSet(Splitter.on(",").omitEmptyStrings().trimResults().split(wfs));
395

    
396
		for (final String wfId : wfFilter) {
397
			final Iterator<Map<String, String>> iterator = this.dnetLogger.find(WorkflowsConstants.LOG_WF_PROFILE_ID, wfId);
398
			StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
399
					.map(this::toProcessInfo)
400
					.forEach(e -> map.put(e.getProcId(), e));
401
		}
402

    
403
		for (final WorkflowProcess proc : this.graphProcessRegistry.listProcesses()) {
404
			if (wfFilter.contains(proc.getProfileId())) {
405
				map.put(proc.getProfileId(), new ProcessInfo(proc));
406
			}
407
		}
408

    
409
		return map.values().stream().sorted(new Comparator<ProcessInfo>() {
410

    
411
			@Override
412
			public int compare(final ProcessInfo p1, final ProcessInfo p2) {
413
				if (p1.getDate() == 0) {
414
					return -1;
415
				} else if (p2.getDate() == 0) {
416
					return 1;
417
				} else {
418
					return Long.compare(p2.getDate(), p1.getDate());
419
				}
420
			}
421
		}).collect(Collectors.toList());
422
	}
423

    
424
	@RequestMapping("/ui/wf/journal_byFamily.find")
425
	public @ResponseBody Collection<ProcessInfo> findWfJournalByFamily(@RequestParam(value = "family", required = true) final String family)
426
			throws IOException {
427

    
428
		final Iterator<Map<String, String>> iterator = this.dnetLogger.find(WorkflowsConstants.LOG_WF_FAMILY, family);
429

    
430
		return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED), false)
431
				.map(this::toProcessInfo)
432
				.collect(Collectors.toList());
433

    
434
	}
435

    
436
	@RequestMapping("/ui/wf/journal.get")
437
	public @ResponseBody ProcessInfo getWfJournalLog(@RequestParam(value = "id", required = true) final String procId) throws Exception {
438
		final WorkflowProcess process = this.graphProcessRegistry.findProcess(procId);
439
		final Map<String, String> logs = this.dnetLogger.findOne("system:processId", procId);
440

    
441
		final ProcessInfo info = (process != null) ? new ProcessInfo(process) : new ProcessInfo(logs);
442

    
443
		if (logs != null && !logs.isEmpty()) {
444
			logs.keySet().stream().sorted().forEach(k -> info.getOutputParams().add(new EnvParam(k, logs.get(k))));
445
		}
446

    
447
		return info;
448
	}
449

    
450
	@RequestMapping(value = "/ui/wf/saveParams.do")
451
	public @ResponseBody boolean saveWorkflowParams(@RequestParam(value = "wf", required = true) final String wfId,
452
			@RequestParam(value = "params", required = true) final String jsonParams) throws Exception {
453

    
454
		final String xml = this.isLookupClient.getProfile(wfId);
455

    
456
		final Map<String, String> params = new Gson().fromJson(jsonParams, new TypeToken<Map<String, String>>() {}.getType());
457

    
458
		return this.isRegistryClient.updateWorkflowProfile(wfId, xml, params);
459
	}
460

    
461
	@RequestMapping(value = "/ui/wf/obtainSubWorkflows.do")
462
	public @ResponseBody List<Map<String, String>> obtainSubWorkflows(@RequestParam(value = "id", required = true) final String wfId) {
463
		return this.isLookupClient.obtainSubWorkflows(wfId);
464
	}
465

    
466
	@RequestMapping(value = "/ui/wf/repohi_wfs.find")
467
	public @ResponseBody List<RepoHIWorkflow> listRepoHIWorkflows(@RequestParam(value = "compliance", required = true) final String compliance,
468
			@RequestParam(value = "type", required = true) final String type) throws ISLookUpException {
469

    
470
		return this.isLookupClient.listRepoHiWorkflows(compliance, type);
471
	}
472

    
473
	@RequestMapping("/ui/wf/repohi.start")
474
	public @ResponseBody List<String> newRepoWorkflow(@RequestParam(value = "id", required = true) final String repoId,
475
			@RequestParam(value = "iface", required = true) final String ifaceId,
476
			@RequestParam(value = "wf", required = true) final String wfId) throws Exception {
477

    
478
		final String procId = this.workflowExecutor.startRepoHiWorkflow(wfId, repoId, ifaceId, new ProcessCallback() {
479

    
480
			@Override
481
			public void onSuccess() {
482
				try {
483
					WorkflowsController.this.serviceLocator.getService(DatasourceManagerService.class).updateActivationStatus(repoId, ifaceId, true);
484
				} catch (final DatasourceManagerServiceException e) {
485
					log.error("Error updating activation status of " + repoId);
486
				}
487
			}
488

    
489
			@Override
490
			public void onFail() {}
491
		});
492

    
493
		return Arrays.asList(procId);
494
	}
495

    
496
	@RequestMapping("/ui/wf/repobye.start")
497
	public @ResponseBody List<String> destroyRepoWorkflow(@RequestParam(value = "wf", required = true) final String wf)
498
			throws Exception {
499

    
500
		final String profile = this.isLookupClient.getProfile(wf);
501
		final Document doc = (new SAXReader()).read(new StringReader(profile));
502
		final String dsId = doc.valueOf("//DATASOURCE/@id");
503
		final String iface = doc.valueOf("//DATASOURCE/@interface");
504

    
505
		final Map<String, String> globalParams = new HashMap<String, String>();
506
		for (final Object o : doc.selectNodes("//CONFIGURATION/PARAMETERS/PARAM")) {
507
			final Element p = (Element) o;
508
			globalParams.put(p.valueOf("@name"), p.getTextTrim());
509
		}
510

    
511
		final Node node = doc.selectSingleNode("//DESTROY_WORKFLOW_TEMPLATE");
512
		if (node == null) {
513
			log.error("DESTROY WF is invalid or missing in profile " + wf);
514
			throw new MSROException("DESTROY WF is invalid or missing in profile " + wf);
515
		}
516

    
517
		final String wfTtemplateId = node.valueOf("@id");
518
		final Map<String, String> params = this.graphLoader.calculateParamsForNode(node, globalParams)
519
				.entrySet()
520
				.stream()
521
				.filter(e -> e.getValue() instanceof String)
522
				.collect(Collectors.toMap(e -> e.getKey(), e -> (String) e.getValue(), (e1, e2) -> e1));
523

    
524
		final String procId =
525
				this.workflowExecutor.startWorkflowTemplate(wfTtemplateId, "REPO_BYE", "REPO_BYE", WorkflowsConstants.DEFAULT_WF_PRIORITY, dsId, iface,
526
						params, new ProcessCallback() {
527

    
528
							@Override
529
							public void onSuccess() {
530
								try {
531
									WorkflowsController.this.isRegistryClient.deleteProfile(wf);
532
								} catch (final ISRegistryException e) {
533
									log.error("Error deleting workflow: " + wf);
534
								}
535
							}
536

    
537
							@Override
538
							public void onFail() {}
539
						});
540

    
541
		return Arrays.asList(procId);
542
	}
543

    
544
	private ProcessInfo toProcessInfo(final Map<String, String> input) {
545
		final ProcessInfo info = new ProcessInfo();
546

    
547
		info.setProcId(input.get(WorkflowsConstants.LOG_WF_PROCESS_ID));
548
		info.setWfId(input.get(WorkflowsConstants.LOG_WF_PROFILE_ID));
549
		info.setName(input.get(WorkflowsConstants.LOG_WF_NAME));
550
		info.setFamily(input.get(WorkflowsConstants.LOG_WF_FAMILY));
551
		info.setDatasource(input.containsKey(WorkflowsConstants.LOG_DATASOURCE_NAME) ? input.get(WorkflowsConstants.LOG_DATASOURCE_NAME) : "");
552
		info.setStatus(input.get(WorkflowsConstants.LOG_WF_PROCESS_STATUS));
553
		info.setDate(NumberUtils.toLong(input.get(LogMessage.LOG_DATE_FIELD), 0));
554
		info.setStartDate(info.getStartDate());
555
		info.setEndDate(info.getEndDate());
556

    
557
		return info;
558

    
559
	}
560

    
561
}
(4-4/4)