1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.exoplatform.services.cms.actions.impl;
18
19 import org.exoplatform.container.component.ComponentPlugin;
20 import org.exoplatform.services.cms.CmsService;
21 import org.exoplatform.services.cms.JcrInputProperty;
22 import org.exoplatform.services.cms.actions.ActionPlugin;
23 import org.exoplatform.services.cms.actions.ActionServiceContainer;
24 import org.exoplatform.services.cms.actions.DMSEvent;
25 import org.exoplatform.services.cms.impl.Utils;
26 import org.exoplatform.services.jcr.RepositoryService;
27 import org.exoplatform.services.jcr.config.RepositoryConfigurationException;
28 import org.exoplatform.services.jcr.core.ManageableRepository;
29 import org.exoplatform.services.jcr.core.nodetype.ExtendedNodeTypeManager;
30 import org.exoplatform.services.jcr.core.nodetype.NodeTypeValue;
31 import org.exoplatform.services.jcr.core.nodetype.PropertyDefinitionValue;
32 import org.exoplatform.services.log.ExoLogger;
33 import org.exoplatform.services.log.Log;
34 import org.picocontainer.Startable;
35
36 import javax.jcr.Node;
37 import javax.jcr.NodeIterator;
38 import javax.jcr.PathNotFoundException;
39 import javax.jcr.PropertyType;
40 import javax.jcr.RepositoryException;
41 import javax.jcr.Session;
42 import javax.jcr.Value;
43 import javax.jcr.nodetype.NodeType;
44 import javax.jcr.nodetype.NodeTypeIterator;
45 import javax.jcr.nodetype.NodeTypeManager;
46 import javax.jcr.nodetype.PropertyDefinition;
47 import javax.jcr.query.InvalidQueryException;
48 import javax.jcr.query.Query;
49 import javax.jcr.query.QueryManager;
50 import javax.jcr.query.QueryResult;
51 import javax.jcr.version.OnParentVersionAction;
52 import java.util.ArrayList;
53 import java.util.Collection;
54 import java.util.HashMap;
55 import java.util.List;
56 import java.util.Map;
57
58
59
60
61
62
63 public class ActionServiceContainerImpl implements ActionServiceContainer, Startable {
64
65
66
67
68 private static final Log LOG = ExoLogger.getLogger(ActionServiceContainerImpl.class.getName());
69
70
71
72
73 private static final String ACTIONABLE = "exo:actionable";
74
75
76
77
78 private static final String ACTION = "exo:action";
79
80
81
82
83 private static final String JOB_NAME_PROP = "exo:jobName";
84
85
86
87
88 private static final String JOB_GROUP_PROP = "exo:jobGroup";
89
90
91
92
93 private static final String JOB_CLASS_PROP = "exo:jobClass";
94
95
96
97
98 private static final String LIFECYCLE_PHASE_PROP = "exo:lifecyclePhase" ;
99
100
101
102
103 private static final String ACTION_QUERY = "//element(*, exo:action)" ;
104
105
106
107
108 private static final String ACTION_SQL_QUERY =
109 "select * from exo:action" ;
110
111
112
113
114 private static final String WHERE_OPERATOR = " where" ;
115
116
117
118
119 private static final String JCR_PATH = " jcr:path" ;
120
121
122
123
124 private static final String LIKE_OPERATOR = " like" ;
125
126
127
128
129 private static final String SINGLE_QUOTE = "'";
130
131
132
133
134 private static final String SCHEDULABLE_MIXIN = "exo:schedulableInfo";
135
136
137
138
139 private static final String EXO_ACTIONS = "exo:actions";
140
141
142
143
144 private static final String ACTION_STORAGE = "exo:actionStorage";
145
146
147
148
149 private static final String EXO_HIDDENABLE = "exo:hiddenable";
150
151
152
153
154 private RepositoryService repositoryService_;
155
156
157
158
159 private CmsService cmsService_;
160
161
162
163
164 private Collection<ComponentPlugin> actionPlugins = new ArrayList<ComponentPlugin>();
165
166
167
168
169
170
171
172 public ActionServiceContainerImpl(RepositoryService repositoryService, CmsService cmsService
173 ) throws Exception {
174 repositoryService_ = repositoryService;
175 cmsService_ = cmsService;
176 }
177
178
179
180
181
182 public void start() {
183 if (LOG.isInfoEnabled()) {
184 LOG.info("Start " + this.getClass().getSimpleName()+ "...");
185 }
186 try {
187 for (ComponentPlugin cPlungin : actionPlugins) {
188 BaseActionPlugin plugin = (BaseActionPlugin) cPlungin;
189 plugin.importPredefinedActionsInJcr();
190 }
191 initiateActionConfiguration();
192 } catch (Exception e) {
193 if (LOG.isErrorEnabled()) {
194 LOG.error("Cannot start ActionServiceContainerImpl", e);
195 }
196 }
197 }
198
199
200
201
202 public void stop() {
203 }
204
205
206
207
208 public void init() {
209 try {
210 for (ComponentPlugin cPlungin : actionPlugins) {
211 BaseActionPlugin plugin = (BaseActionPlugin) cPlungin;
212 plugin.reImportPredefinedActionsInJcr();
213 }
214 reInitiateActionConfiguration();
215 } catch (Exception e) {
216 if (LOG.isErrorEnabled()) {
217 LOG.error("Cannot initialize the ActionServiceContainerImpl", e);
218 }
219 }
220 }
221
222
223
224
225 public Collection<String> getActionPluginNames() {
226 Collection<String> actionPluginNames = new ArrayList<String>(actionPlugins.size());
227 for (ComponentPlugin plugin : actionPlugins) {
228 actionPluginNames.add(plugin.getName());
229 }
230 return actionPluginNames;
231 }
232
233
234
235
236 public ActionPlugin getActionPlugin(String actionsServiceName) {
237 for (ComponentPlugin plugin : actionPlugins) {
238 if (plugin.getName().equals(actionsServiceName))
239 return (ActionPlugin) plugin;
240 }
241 return null;
242 }
243
244
245
246
247
248
249
250
251
252
253 @SuppressWarnings("unchecked")
254 public void createActionType(String actionTypeName, String parentActionTypeName, String executable, String actionLabel,
255 List<String> variableNames, boolean isMoveType, boolean isUpdate) throws Exception {
256 NodeTypeValue nodeTypeValue = new NodeTypeValue();
257 nodeTypeValue.setName(actionTypeName);
258
259 List<String> superTypes = new ArrayList<String>();
260 superTypes.add(parentActionTypeName);
261 if (isMoveType)
262 superTypes.add("exo:move");
263 nodeTypeValue.setDeclaredSupertypeNames(superTypes);
264
265 List propDefs = new ArrayList();
266 PropertyDefinitionValue propDef = null;
267 for (String variableName : variableNames) {
268 propDef = createPropertyDef(variableName);
269 propDefs.add(propDef);
270 }
271 propDef = createPropertyDef(getActionPluginForActionType(parentActionTypeName).getExecutableDefinitionName());
272 List scriptDefaultValues = new ArrayList();
273 scriptDefaultValues.add(executable);
274 propDef.setDefaultValueStrings(scriptDefaultValues);
275 propDef.setMandatory(true);
276 propDefs.add(propDef);
277 propDef = createPropertyDef(getActionPluginForActionType(parentActionTypeName).getActionExecutableLabel());
278 List labelDefaultValues = new ArrayList();
279 labelDefaultValues.add(actionLabel);
280 propDef.setDefaultValueStrings(labelDefaultValues);
281 propDef.setMandatory(true);
282 propDefs.add(propDef);
283
284 nodeTypeValue.setDeclaredPropertyDefinitionValues(propDefs);
285 nodeTypeValue.setDeclaredChildNodeDefinitionValues(new ArrayList());
286 ExtendedNodeTypeManager ntmanager = repositoryService_.getCurrentRepository().getNodeTypeManager();
287 if(isUpdate) ntmanager.registerNodeType(nodeTypeValue, ExtendedNodeTypeManager.REPLACE_IF_EXISTS);
288 ntmanager.registerNodeType(nodeTypeValue, ExtendedNodeTypeManager.IGNORE_IF_EXISTS);
289 }
290
291
292
293
294
295
296
297
298 @SuppressWarnings("unchecked")
299 private PropertyDefinitionValue createPropertyDef(String name) {
300 PropertyDefinitionValue def = new PropertyDefinitionValue();
301 def.setName(name);
302 def.setRequiredType(PropertyType.STRING);
303 def.setMandatory(false);
304 def.setMultiple(false);
305 def.setReadOnly(false);
306 def.setAutoCreate(false);
307 def.setOnVersion(OnParentVersionAction.COPY);
308 def.setValueConstraints(new ArrayList());
309 def.setDefaultValueStrings(new ArrayList());
310 return def;
311 }
312
313
314
315
316
317
318
319 public Collection<NodeType> getCreatedActionTypes(String repository) throws Exception {
320 Collection<NodeType> createsActions = new ArrayList<NodeType>();
321 NodeTypeManager ntmanager = repositoryService_.getCurrentRepository().getNodeTypeManager();
322 for(NodeTypeIterator iter = ntmanager.getAllNodeTypes();iter.hasNext();) {
323 NodeType nt = (NodeType) iter.next();
324 String name = nt.getName();
325 if (nt.isNodeType(ACTION) && !isAbstractType(name) &&
326 !Utils.getAllEditedConfiguredData("ActionTypeList", "EditedConfiguredActionType", true).contains(name)) {
327 createsActions.add(nt);
328 }
329 }
330 return createsActions;
331 }
332
333
334
335
336
337
338
339 private boolean isAbstractType(String name) {
340 for (ComponentPlugin plugin : actionPlugins) {
341 if (plugin.getName().equals(name))
342 return true;
343 }
344 return false;
345 }
346
347
348
349
350
351
352
353
354 private Session getSystemSession(String workspace) throws RepositoryException,
355 RepositoryConfigurationException {
356 ManageableRepository jcrRepository = repositoryService_.getCurrentRepository();
357 return jcrRepository.getSystemSession(workspace);
358 }
359
360
361
362
363 public ActionPlugin getActionPluginForActionType(String actionTypeName) {
364 for (ComponentPlugin plugin : actionPlugins) {
365 String actionServiceName = plugin.getName();
366 ActionPlugin actionService = getActionPlugin(actionServiceName);
367 if (actionService.isActionTypeSupported(actionTypeName)
368 || actionServiceName.equals(actionTypeName))
369 return actionService;
370 }
371 return null;
372 }
373
374
375
376
377 public Node getAction(Node node, String actionName) throws Exception {
378 if (node.hasNode(EXO_ACTIONS + "/"+actionName)) {
379 return node.getNode(EXO_ACTIONS + "/"+ actionName);
380 }
381 return null;
382 }
383
384
385
386
387 public boolean hasActions(Node node) throws Exception {
388 return node.isNodeType(ACTIONABLE);
389 }
390
391
392
393
394 public List<Node> getActions(Node node) throws Exception {
395 return getActions(node, null);
396 }
397
398
399
400
401 public List<Node> getCustomActionsNode(Node node, String lifecyclePhase) throws Exception {
402 try {
403 return getActions(node, lifecyclePhase) ;
404 } catch(Exception item) {
405 return null ;
406 }
407 }
408
409
410
411
412 public List<Node> getActions(Node node, String lifecyclePhase) throws Exception {
413 List<Node> actions = new ArrayList<Node>();
414 Node actionStorage = null;
415 try{
416 actionStorage = node.getNode(EXO_ACTIONS);
417 }catch (Exception e) {
418 return actions;
419 }
420 for (NodeIterator iter = actionStorage.getNodes(); iter.hasNext();) {
421 Node tmpNode = iter.nextNode();
422 if (tmpNode.isNodeType(ACTION)
423 && (lifecyclePhase == null || parseValuesToList(
424 tmpNode.getProperty(LIFECYCLE_PHASE_PROP).getValues()).contains(lifecyclePhase))) {
425 actions.add(tmpNode);
426 }
427 }
428 return actions;
429 }
430
431
432
433
434 public void removeAction(Node node, String repository) throws Exception {
435 if(!node.isNodeType(ACTIONABLE)) return ;
436 List<Node> actions = getActions(node);
437 for (Node action : actions) {
438 removeAction(node, action.getName(), repository);
439 }
440 }
441
442
443
444
445 public void removeAction(Node node, String actionName, String repository) throws Exception {
446 if(!node.isNodeType(ACTIONABLE)) return ;
447 Node action2Remove = node.getNode(EXO_ACTIONS+ "/" + actionName);
448 String[] lifecyclePhase = parseValuesToArray(action2Remove.getProperty(LIFECYCLE_PHASE_PROP)
449 .getValues());
450 String jobName = null, jobGroup = null, jobClassName = null;
451 if (action2Remove.isNodeType(SCHEDULABLE_MIXIN)) {
452 jobName = action2Remove.getProperty(JOB_NAME_PROP).getString();
453 jobGroup = action2Remove.getProperty(JOB_GROUP_PROP).getString();
454 jobClassName = action2Remove.getProperty(JOB_CLASS_PROP).getString();
455 }
456 String actionTypeName = action2Remove.getPrimaryNodeType().getName();
457 String actionPath = action2Remove.getPath();
458 for (ComponentPlugin plugin : actionPlugins) {
459 String actionServiceName = plugin.getName();
460 ActionPlugin actionService = getActionPlugin(actionServiceName);
461 if (actionService.isActionTypeSupported(actionTypeName)) {
462 if ((DMSEvent.getEventTypes(lifecyclePhase) & DMSEvent.SCHEDULE) > 0) {
463 actionService.removeActivationJob(jobName, jobGroup, jobClassName);
464 }
465 actionService.removeObservation(repository, actionPath);
466 }
467 }
468 action2Remove.remove();
469 node.save();
470 }
471
472
473
474
475 public void addAction(Node storeActionNode,
476 String actionType,
477 boolean isDeep,
478 String[] uuid,
479 String[] nodeTypeNames,
480 Map mappings) throws Exception {
481 Node actionsNode = null;
482 try {
483 actionsNode = storeActionNode.getNode(EXO_ACTIONS);
484 } catch (PathNotFoundException e) {
485 actionsNode = storeActionNode.addNode(EXO_ACTIONS,ACTION_STORAGE) ;
486 actionsNode.addMixin(EXO_HIDDENABLE) ;
487 storeActionNode.save();
488 }
489 if (!storeActionNode.isNodeType(ACTIONABLE)) {
490 storeActionNode.addMixin(ACTIONABLE);
491 storeActionNode.save();
492 }
493 String newActionPath = cmsService_.storeNode(actionType, actionsNode, mappings,true);
494 storeActionNode.save();
495 String srcWorkspace = storeActionNode.getSession().getWorkspace().getName();
496
497 String srcPath = storeActionNode.getPath();
498 ActionPlugin actionService = getActionPluginForActionType(actionType);
499 if (actionService == null)
500 throw new ClassNotFoundException("Not found any action's service compatible with action type "+actionType) ;
501 try {
502 actionService.addAction(actionType, srcWorkspace, srcPath, isDeep, uuid, nodeTypeNames, mappings);
503 } catch (Exception e) {
504 if (LOG.isErrorEnabled()) {
505 LOG.error(e);
506 }
507 Session session = getSystemSession(storeActionNode.getSession().getWorkspace().getName());
508 Node actionNode = (Node) session.getItem(newActionPath);
509 actionNode.remove();
510 session.save();
511 session.logout();
512 throw e;
513 }
514 }
515
516
517
518
519 public void addAction(Node storeActionNode, String actionType, Map mappings) throws Exception {
520 boolean isDeep = true;
521 String[] nodeTypeName = null;
522 String[] uuid = null;
523 if (mappings.containsKey("/node/exo:isDeep")) {
524 isDeep = Boolean.valueOf(((JcrInputProperty)mappings.get("/node/exo:isDeep")).getValue().toString());
525 }
526 if (mappings.containsKey("/node/exo:uuid")) {
527 uuid = (String[]) ((JcrInputProperty) mappings.get("/node/exo:uuid")).getValue();
528 if(uuid.length == 0) uuid = null;
529 }
530 if (mappings.containsKey("/node/exo:nodeTypeName")) {
531 nodeTypeName = (String[]) ((JcrInputProperty) mappings.get("/node/exo:nodeTypeName"))
532 .getValue();
533 if(nodeTypeName.length == 0) {
534 nodeTypeName = null;
535 mappings.remove("/node/exo:nodeTypeName");
536 }
537 }
538 addAction(storeActionNode, actionType, isDeep, uuid, nodeTypeName, mappings);
539 }
540
541
542
543
544
545
546
547
548
549 public void executeAction(String userId, Node node, String actionName) throws Exception {
550 Map<String, String> variables = new HashMap<String, String>();
551 variables.put("initiator", userId);
552 variables.put("actionName", actionName);
553 variables.put("nodePath", node.getPath());
554 variables.put("srcWorkspace", node.getSession().getWorkspace().getName());
555 variables.put("srcPath", node.getPath());
556
557 NodeType nodeType = node.getPrimaryNodeType();
558 String nodeTypeName = nodeType.getName();
559 variables.put("document-type", nodeTypeName);
560 Node actionNode = getAction(node, actionName);
561 NodeType actionNodeType = actionNode.getPrimaryNodeType();
562 fillVariables(actionNode, actionNodeType, variables);
563
564 NodeType[] actionMixinTypes = actionNode.getMixinNodeTypes();
565
566 for (int i = 0; i < actionMixinTypes.length; i++) {
567 NodeType mixinType = actionMixinTypes[i];
568 fillVariables(actionNode, mixinType, variables);
569 }
570
571 executeAction(userId, node, actionName, variables);
572 }
573
574
575
576
577
578
579
580
581
582 private void fillVariables(Node actionNode, NodeType nodeType, Map variables) throws Exception {
583 for(PropertyDefinition def:nodeType.getDeclaredPropertyDefinitions()) {
584 String propName = def.getName();
585 if (actionNode.hasProperty(propName)) {
586 if(actionNode.getProperty(propName).getDefinition().isMultiple()) {
587
588 } else {
589 String propValue = actionNode.getProperty(propName).getString();
590 variables.put(propName, propValue);
591 }
592 }
593 }
594 }
595
596
597
598
599
600
601
602
603
604 public void executeAction(String userId, Node node, String actionName, Map variables) throws Exception {
605 if (!node.isNodeType(ACTIONABLE)) return ;
606 Node actionNode = getAction(node, actionName);
607 String actionTypeName = actionNode.getPrimaryNodeType().getName();
608 for (ComponentPlugin plugin : actionPlugins) {
609 String actionServiceName = plugin.getName();
610 ActionPlugin actionPlugin = getActionPlugin(actionServiceName);
611 if (actionPlugin.isActionTypeSupported(actionTypeName)) {
612 actionPlugin.executeAction(userId, actionNode, variables);
613 }
614 }
615 }
616
617
618
619
620
621 public void addPlugin(ComponentPlugin plugin) { actionPlugins.add(plugin); }
622
623
624
625
626
627
628 public ComponentPlugin removePlugin(String pluginName) {
629 return null;
630 }
631
632
633
634
635
636 public Collection<ComponentPlugin> getPlugins() { return actionPlugins; }
637
638
639
640
641
642
643 private void initiateActionConfiguration() throws Exception {
644 ManageableRepository jcrRepository = null ;
645 jcrRepository = repositoryService_.getCurrentRepository();
646 String[] workspaces = jcrRepository.getWorkspaceNames();
647 for (String workspace : workspaces) {
648 Session session = jcrRepository.getSystemSession(workspace);
649 QueryManager queryManager = null;
650 try {
651 queryManager = session.getWorkspace().getQueryManager();
652 } catch (RepositoryException e) {
653 if (LOG.isWarnEnabled()) {
654 LOG.warn("ActionServiceContainer - Query Manager Factory of workspace "
655 + workspace + " not found. Check configuration.", e);
656 }
657 }
658 if (queryManager == null) {
659 session.logout();
660 continue;
661 }
662 initAction(queryManager, workspace) ;
663 session.logout();
664 }
665 }
666
667
668
669
670
671
672 private void reInitiateActionConfiguration() throws Exception {
673 ManageableRepository jcrRepository = repositoryService_.getCurrentRepository();
674 for (String workspace : jcrRepository.getWorkspaceNames()) {
675 Session session = jcrRepository.getSystemSession(workspace);
676 QueryManager queryManager = null;
677 try {
678 queryManager = session.getWorkspace().getQueryManager();
679 } catch (RepositoryException e) {
680 if (LOG.isWarnEnabled()) {
681 LOG.warn("ActionServiceContainer - Query Manager Factory of workspace "
682 + workspace + " not found. Check configuration.", e);
683 }
684 }
685 if (queryManager == null) {
686 session.logout();
687 continue;
688 }
689 initAction(queryManager, workspace) ;
690 session.logout();
691 }
692 }
693
694
695
696
697
698
699
700
701 private void initAction(QueryManager queryManager, String workspace) throws Exception {
702 try {
703 Query query = queryManager.createQuery(ACTION_QUERY, Query.XPATH);
704 QueryResult queryResult = query.execute();
705 for (NodeIterator iter = queryResult.getNodes(); iter.hasNext();) {
706 Node actionNode = iter.nextNode();
707 String[] lifecyclePhase = parseValuesToArray(actionNode.getProperty(LIFECYCLE_PHASE_PROP)
708 .getValues());
709 String actionType = actionNode.getPrimaryNodeType().getName();
710 for (ComponentPlugin plugin : actionPlugins) {
711 String actionServiceName = plugin.getName();
712 ActionPlugin actionService = getActionPlugin(actionServiceName);
713 if (actionService.isActionTypeSupported(actionType)) {
714 if (DMSEvent.getEventTypes(lifecyclePhase) == DMSEvent.READ)
715 continue;
716 if ((DMSEvent.getEventTypes(lifecyclePhase) & DMSEvent.SCHEDULE) > 0) {
717 actionService.reScheduleActivations(actionNode);
718 }
719 if (DMSEvent.getEventTypes(lifecyclePhase) == DMSEvent.SCHEDULE)
720 continue;
721 actionService.initiateActionObservation(actionNode);
722 }
723 }
724 }
725 } catch (Exception e) {
726 if (LOG.isErrorEnabled()) {
727 LOG.error(">>>> Can not launch action listeners for workspace: "
728 + workspace + " in current repository", e);
729 }
730 }
731 }
732
733
734
735
736 public void initiateObservation(Node node) throws Exception {
737 try {
738 Session session = node.getSession();
739 QueryManager queryManager = session.getWorkspace().getQueryManager();
740 String queryStr;
741 Query query = null;
742 try {
743 if (!"/".equals(node.getPath())) {
744 queryStr = "/jcr:root" + node.getPath() + ACTION_QUERY;
745 } else {
746 queryStr = ACTION_QUERY;
747 }
748 query = queryManager.createQuery(queryStr, Query.XPATH);
749 } catch(InvalidQueryException invalid) {
750
751 if (!"/".equals(node.getPath())) {
752 queryStr = ACTION_SQL_QUERY + WHERE_OPERATOR + JCR_PATH + LIKE_OPERATOR
753 + SINGLE_QUOTE + node.getPath() + "/" + "%" + SINGLE_QUOTE;
754 } else {
755 queryStr = ACTION_SQL_QUERY;
756 }
757 query = queryManager.createQuery(queryStr, Query.SQL);
758 }
759 QueryResult queryResult = query.execute();
760 for (NodeIterator iter = queryResult.getNodes(); iter.hasNext();) {
761 Node actionNode = iter.nextNode();
762 try {
763 String actionType = actionNode.getPrimaryNodeType().getName();
764 for (ComponentPlugin plugin : actionPlugins) {
765 String actionServiceName = plugin.getName();
766 ActionPlugin actionService = getActionPlugin(actionServiceName);
767 if (actionService.isActionTypeSupported(actionType)) {
768 actionService.initiateActionObservation(actionNode);
769 }
770 }
771 } catch (Exception e) {
772 if (LOG.isErrorEnabled()) {
773 LOG.error("Can not launch action listeners named is " + actionNode.getPath(), e);
774 }
775 }
776 }
777 } catch (Exception ex) {
778 if (LOG.isErrorEnabled()) {
779 LOG.error("Can not launch action listeners inside " + node.getPath() + " node.", ex);
780 }
781 }
782 }
783
784
785
786
787
788
789
790 private String[] parseValuesToArray(Value[] values) throws Exception {
791 return parseValuesToList(values).toArray(new String[0]);
792 }
793
794
795
796
797
798
799
800 private List<String> parseValuesToList(Value[] values) throws Exception {
801 List<String> lstValues = new ArrayList<String>();
802 for(Value value : values) {
803 lstValues.add(value.getString());
804 }
805 return lstValues;
806 }
807 }