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 javax.annotation.Resource;
10
import javax.servlet.http.HttpServletResponse;
11

    
12
import com.google.common.base.Function;
13
import com.google.common.base.Splitter;
14
import com.google.common.collect.Iterators;
15
import com.google.common.collect.Lists;
16
import com.google.common.collect.Maps;
17
import com.google.common.collect.Sets;
18
import com.google.gson.Gson;
19
import com.google.gson.reflect.TypeToken;
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.EnvParam;
25
import eu.dnetlib.functionality.modular.ui.workflows.objects.ProcessInfo;
26
import eu.dnetlib.functionality.modular.ui.workflows.objects.WorkflowItem;
27
import eu.dnetlib.functionality.modular.ui.workflows.objects.WorkflowUpdateInfo;
28
import eu.dnetlib.functionality.modular.ui.workflows.objects.sections.WorkflowSectionGrouper;
29
import eu.dnetlib.functionality.modular.ui.workflows.util.ISLookupClient;
30
import eu.dnetlib.functionality.modular.ui.workflows.util.ISRegistryClient;
31
import eu.dnetlib.miscutils.functional.xml.ApplyXsltDom4j;
32
import eu.dnetlib.msro.logging.DnetLogger;
33
import eu.dnetlib.msro.logging.LogMessage;
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.util.WorkflowsConstants;
38
import eu.dnetlib.rmi.data.ProtocolParameter;
39
import eu.dnetlib.rmi.enabling.ISLookUpException;
40
import org.apache.commons.io.IOUtils;
41
import org.apache.commons.lang3.StringUtils;
42
import org.apache.commons.lang3.math.NumberUtils;
43
import org.apache.commons.logging.Log;
44
import org.apache.commons.logging.LogFactory;
45
import org.dom4j.Document;
46
import org.dom4j.Element;
47
import org.dom4j.Node;
48
import org.dom4j.io.SAXReader;
49
import org.springframework.beans.factory.annotation.Value;
50
import org.springframework.core.io.ClassPathResource;
51
import org.springframework.stereotype.Controller;
52
import org.springframework.web.bind.annotation.RequestMapping;
53
import org.springframework.web.bind.annotation.RequestParam;
54
import org.springframework.web.bind.annotation.ResponseBody;
55

    
56
/**
57
 * Web controller for the UI
58
 *
59
 * @author Michele Artini
60
 */
61

    
62
@Controller
63
public class WorkflowsController extends AbstractAjaxController {
64

    
65
	private static final Log log = LogFactory.getLog(WorkflowsController.class);
66

    
67
	@Resource
68
	private ISLookupClient isLookupClient;
69

    
70
	@Resource
71
	private ISRegistryClient isRegistryClient;
72

    
73
	@Resource
74
	private RepoUIUtils repoUIUtils;
75

    
76
	@Resource
77
	private ProcessRegistry graphProcessRegistry;
78

    
79
	@Resource
80
	private WorkflowSectionGrouper workflowSectionGrouper;
81

    
82
	@Resource
83
	private WorkflowExecutor workflowExecutor;
84

    
85
	@Resource(name = "msroWorkflowLogger")
86
	private DnetLogger dnetLogger;
87

    
88
	@Value("${repo.ui.compatibilityLevels.vocabulary}")
89
	private String compatibilityLevelsVocabulary;
90

    
91
	@RequestMapping("/ui/wf/list_workflows.json")
92
	public @ResponseBody List<WorkflowItem> listWorflowsForSection(@RequestParam(value = "section", required = false) final String sectionName,
93
			@RequestParam(value = "dsId", required = false) final String dsId)
94
			throws ISLookUpException, IOException {
95
		if (sectionName != null) {
96
			return this.workflowSectionGrouper.listWorflowsForSection(sectionName);
97
		} else if (dsId != null) {
98
			return this.workflowSectionGrouper.listWorflowsForDatasource(dsId);
99
		} else {
100
			return Lists.newArrayList();
101
		}
102
	}
103

    
104
	@RequestMapping("/ui/wf/workflow.html")
105
	public void getWorkflowHtml(final HttpServletResponse res, @RequestParam(value = "id", required = true) final String id) throws Exception {
106
		final String profile = this.isLookupClient.getProfile(id);
107
		final Document doc = (new SAXReader()).read(new StringReader(profile));
108
		final Element dsNode = (Element) doc.selectSingleNode("//DATASOURCE");
109
		if (dsNode != null) {
110
			dsNode.addAttribute("name", this.isLookupClient.getDatasourceName(dsNode.valueOf("@id")));
111
		}
112
		final ApplyXsltDom4j xslt = new ApplyXsltDom4j(new ClassPathResource("/eu/dnetlib/functionality/modular/ui/workflows/xslt/wf_profile2html.xslt"));
113
		res.setContentType("text/html");
114
		IOUtils.copy(new StringReader(xslt.apply(doc)), res.getOutputStream());
115
	}
116

    
117
	@RequestMapping("/ui/wf/repoApi.html")
118
	public void getWorkflowHtml(final HttpServletResponse res,
119
			@RequestParam(value = "repoId", required = true) final String repoId,
120
			@RequestParam(value = "ifaceId", required = true) final String ifaceId) throws Exception {
121

    
122
		final String profile = this.isLookupClient.getRepoProfile(repoId);
123

    
124
		final SAXReader reader = new SAXReader();
125
		final Document doc = reader.read(new StringReader(profile));
126

    
127
		final Node ifcNode = doc.selectSingleNode("//INTERFACE[@id = '" + ifaceId + "']");
128
		final String protocol = ifcNode.valueOf("./ACCESS_PROTOCOL");
129
		final Element extra = doc.getRootElement().addElement("extra_info");
130

    
131
		final Element compLevels = extra.addElement("compatibilityLevels");
132
		for (final VocabularyEntry e : this.repoUIUtils.fetchVocabularyTerms(this.compatibilityLevelsVocabulary)) {
133
			final Element l = compLevels.addElement("level");
134
			l.setText(e.getName());
135
		}
136

    
137
		final Element parameters = extra.addElement("parameters");
138
		for (final ProtocolParameter pp : this.repoUIUtils.listParametersForProtocol(protocol)) {
139
			final Element p = parameters.addElement("param");
140
			p.addAttribute("name", pp.getName());
141
			p.addAttribute("type", pp.getType().toString());
142
			p.setText(ifcNode.valueOf("./ACCESS_PROTOCOL/@" + pp.getName()));
143

    
144
			if (StringUtils.isNotBlank(pp.getRegex())) {
145
				p.addAttribute("regex", pp.getRegex());
146
			}
147
			if (pp.isFunctionPopulated()) {
148
				p.addAttribute("functionPopulated", "true");
149
			}
150
			if (pp.isOptional()) {
151
				p.addAttribute("optional", "true");
152
			}
153
		}
154
		final Element extraFields = extra.addElement("extraFields");
155
		for (final Object o : ifcNode.selectNodes("./INTERFACE_EXTRA_FIELD")) {
156
			final Element f = extraFields.addElement("field");
157
			f.addAttribute("name", ((Node) o).valueOf("@name"));
158
			f.setText(((Node) o).getText());
159
		}
160

    
161
		final Element wfs = extra.addElement("workflows");
162
		for (final WorkflowItem item : this.isLookupClient.listWorflowsForApi(repoId, ifaceId)) {
163
			final Element wf = wfs.addElement("workflow");
164
			wf.addAttribute("id", item.getWfId());
165
			wf.addAttribute("name", item.getName());
166
			wf.addAttribute("description", item.getDesc());
167
		}
168

    
169
		final Map<String, String> params = new HashMap<String, String>();
170
		params.put("profileId", doc.valueOf("//RESOURCE_IDENTIFIER/@value"));
171
		params.put("ifaceId", ifaceId);
172
		params.put("protocol", protocol);
173
		params.put("baseUrl", ifcNode.valueOf("./BASE_URL"));
174
		params.put("prefix", doc.valueOf(".//FIELD[./key = 'NamespacePrefix']/value"));
175
		params.put("typology", ifcNode.valueOf("@typology"));
176
		params.put("compliance", ifcNode.valueOf("@compliance"));
177
		params.put("overrideCompliance", ifcNode.valueOf("./INTERFACE_EXTRA_FIELD[@name='overriding_compliance']"));
178

    
179
		doc.selectSingleNode("/RESOURCE_PROFILE/HEADER").detach();
180
		doc.selectSingleNode("/RESOURCE_PROFILE/BODY/CONFIGURATION/INTERFACES").detach();
181
		doc.selectSingleNode("/RESOURCE_PROFILE/BODY/CONFIGURATION/EXTRA_FIELDS").detach();
182
		doc.selectSingleNode("/RESOURCE_PROFILE/BODY/QOS").detach();
183
		doc.selectSingleNode("/RESOURCE_PROFILE/BODY/STATUS").detach();
184
		doc.selectSingleNode("/RESOURCE_PROFILE/BODY/SECURITY_PARAMETERS").detach();
185
		doc.selectSingleNode("/RESOURCE_PROFILE/BODY/BLACKBOARD").detach();
186

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

    
189
		res.setContentType("text/html");
190

    
191
		IOUtils.copy(new StringReader(xslt.apply(doc)), res.getOutputStream());
192
	}
193

    
194
	@RequestMapping("/ui/wf/wf.start")
195
	public @ResponseBody ProcessInfo startWorkflow(@RequestParam(value = "id", required = true) final String id) throws Exception {
196
		final String procId = this.workflowExecutor.startWorkflow(id, null);
197
		final WorkflowProcess process = this.graphProcessRegistry.findProcess(id);
198
		if (process != null) {
199
			return new ProcessInfo(process);
200
		} else {
201
			return new ProcessInfo(procId);
202
		}
203
	}
204

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

    
208
		final WorkflowUpdateInfo info = (new Gson()).fromJson(json, WorkflowUpdateInfo.class);
209

    
210
		log.info("Updating workflow " + info.getWfId());
211

    
212
		final String xml = this.isLookupClient.getProfile(info.getWfId());
213
		final boolean res = this.isRegistryClient.updateWorkflowProfile(info.getWfId(), xml, info);
214

    
215
		return res;
216
	}
217

    
218
	@RequestMapping("/ui/wf/proc.kill")
219
	public @ResponseBody boolean killProcessWorkflow(@RequestParam(value = "id", required = true) final String id) throws Exception {
220
		this.graphProcessRegistry.findProcess(id).kill();
221
		return true;
222
	}
223

    
224
	@RequestMapping("/ui/wf/journal.range")
225
	public @ResponseBody Collection<ProcessInfo> rangeWfJournal(@RequestParam(value = "start", required = true) final String start,
226
			@RequestParam(value = "end", required = true) final String end) throws Exception {
227

    
228
		final Map<String, ProcessInfo> res = Maps.newHashMap();
229

    
230
		final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
231
		final LocalDateTime startDate = LocalDate.parse(start, formatter).atTime(0, 0);
232
		final LocalDateTime endDate = LocalDate.parse(end, formatter).atTime(23, 59);
233

    
234
		final Iterator<ProcessInfo> iter = Iterators.transform(this.dnetLogger.range(startDate, endDate), new JournalEntryFunction());
235
		while (iter.hasNext()) {
236
			final ProcessInfo e = iter.next();
237
			e.setProcId("LOG: " + e.getProcId());
238
			res.put(e.getProcId(), e);
239
		}
240

    
241
		final LocalDateTime now = LocalDateTime.now();
242
		if (startDate.isBefore(now) && endDate.isAfter(now)) {
243
			for (final WorkflowProcess proc : this.graphProcessRegistry.listProcesses()) {
244
				final ProcessInfo p = new ProcessInfo(proc);
245

    
246
				p.setProcId("REG: " + p.getProcId());
247

    
248
				res.put(proc.getId(), p);
249

    
250
			}
251
		}
252

    
253
		return res.values();
254

    
255
	}
256

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

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

    
263
		for (final String wfId : wfFilter) {
264
			final Iterator<ProcessInfo> iter =
265
					Iterators.transform(this.dnetLogger.find(WorkflowsConstants.LOG_WF_PROFILE_ID, wfId), new JournalEntryFunction());
266
			while (iter.hasNext()) {
267
				final ProcessInfo e = iter.next();
268
				map.put(e.getProcId(), e);
269
			}
270
		}
271

    
272
		for (final WorkflowProcess proc : this.graphProcessRegistry.listProcesses()) {
273
			if (wfFilter.contains(proc.getId())) {
274
				map.put(proc.getId(), new ProcessInfo(proc));
275
			}
276
		}
277

    
278
		final List<ProcessInfo> res = Lists.newArrayList(map.values());
279

    
280
		Collections.sort(res, new Comparator<ProcessInfo>() {
281

    
282
			@Override
283
			public int compare(final ProcessInfo p1, final ProcessInfo p2) {
284
				if (p1.getDate() == 0) {
285
					return -1;
286
				} else if (p2.getDate() == 0) {
287
					return 1;
288
				} else {
289
					return Long.compare(p2.getDate(), p1.getDate());
290
				}
291
			}
292
		});
293

    
294
		return res;
295
	}
296

    
297
	@RequestMapping("/ui/wf/journal_byFamily.find")
298
	public @ResponseBody Collection<ProcessInfo> findWfJournalByFamily(@RequestParam(value = "family", required = true) final String family)
299
			throws IOException {
300
		final Iterator<ProcessInfo> iter =
301
				Iterators.transform(this.dnetLogger.find(WorkflowsConstants.LOG_WF_FAMILY, family), new JournalEntryFunction());
302
		return Lists.newArrayList(iter);
303
	}
304

    
305
	@RequestMapping("/ui/wf/journal.get")
306
	public @ResponseBody ProcessInfo getWfJournalLog(@RequestParam(value = "id", required = true) final String procId) throws Exception {
307
		final WorkflowProcess process = this.graphProcessRegistry.findProcess(procId);
308
		final ProcessInfo info = (process != null) ? new ProcessInfo(process) : new ProcessInfo();
309

    
310
		final Map<String, String> logs = this.dnetLogger.findOne("system:processId", procId);
311

    
312
		if (logs != null && !logs.isEmpty()) {
313
			final List<String> keys = Lists.newArrayList(logs.keySet());
314
			Collections.sort(keys);
315
			for (final String k : keys) {
316
				info.getCurrentEnv().add(new EnvParam(k, logs.get(k)));
317
			}
318
		}
319

    
320
		return info;
321
	}
322

    
323
	@RequestMapping(value = "/ui/wf/saveParams.do")
324
	public @ResponseBody boolean saveWorkflowParams(@RequestParam(value = "wf", required = true) final String wfId,
325
			@RequestParam(value = "params", required = true) final String jsonParams) throws Exception {
326

    
327
		final String xml = this.isLookupClient.getProfile(wfId);
328

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

    
331
		return this.isRegistryClient.updateWorkflowProfile(wfId, xml, params);
332
	}
333

    
334
	@RequestMapping(value = "/ui/wf/obtainSubWorkflows.do")
335
	public @ResponseBody List<Map<String, String>> obtainSubWorkflows(@RequestParam(value = "id", required = true) final String wfId) {
336
		return this.isLookupClient.obtainSubWorkflows(wfId);
337
	}
338

    
339
	@RequestMapping(value = "/ui/wf/repohi_wfs.find")
340
	public @ResponseBody List<RepoHIWorkflow> listRepoHIWorkflows(@RequestParam(value = "compliance", required = true) final String compliance,
341
			@RequestParam(value = "type", required = true) final String type) throws ISLookUpException {
342

    
343
		return this.isLookupClient.listRepoHiWorkflows(compliance, type);
344
	}
345

    
346
	@RequestMapping("/ui/wf/repohi.start")
347
	public @ResponseBody List<String> newRepoWorkflow(@RequestParam(value = "id", required = true) final String repoId,
348
			@RequestParam(value = "iface", required = true) final String ifaceId,
349
			@RequestParam(value = "wf", required = true) final String wfId) throws Exception {
350

    
351
		return Lists.newArrayList(this.workflowExecutor.startRepoHiWorkflow(wfId, repoId, ifaceId, null));
352
	}
353

    
354
	@RequestMapping("/ui/wf/repobye.start")
355
	public @ResponseBody List<String> destroyRepoWorkflow(@RequestParam(value = "destroyWf", required = true) final String destroyWfId)
356
			throws Exception {
357
		return Lists.newArrayList(this.workflowExecutor.startWorkflow(destroyWfId, null));
358
	}
359

    
360
	private class JournalEntryFunction implements Function<Map<String, String>, ProcessInfo> {
361

    
362
		@Override
363
		public ProcessInfo apply(final Map<String, String> input) {
364
			final ProcessInfo info = new ProcessInfo();
365

    
366
			info.setProcId(input.get(WorkflowsConstants.LOG_WF_PROCESS_ID));
367
			info.setWfId(input.get(WorkflowsConstants.LOG_WF_PROFILE_ID));
368
			info.setName(input.get(WorkflowsConstants.LOG_WF_NAME));
369
			info.setFamily(input.get(WorkflowsConstants.LOG_WF_FAMILY));
370
			info.setDatasource(input.containsKey(WorkflowsConstants.LOG_DATASOURCE_NAME) ? input.get(WorkflowsConstants.LOG_DATASOURCE_NAME) : "");
371
			info.setStatus(input.get(WorkflowsConstants.LOG_WF_PROCESS_STATUS));
372
			info.setDate(NumberUtils.toLong(input.get(LogMessage.LOG_DATE_FIELD), 0));
373
			info.setStartDate(info.getStartDate());
374
			info.setEndDate(info.getEndDate());
375

    
376
			return info;
377
		}
378
	}
379

    
380
}
(4-4/4)