View Javadoc
1   /*
2    * Copyright (C) 2003-2007 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.services.cms.watch.impl;
18  
19  import java.util.ArrayList;
20  import java.util.List;
21  
22  import javax.jcr.Node;
23  import javax.jcr.NodeIterator;
24  import javax.jcr.Session;
25  import javax.jcr.Value;
26  import javax.jcr.nodetype.NodeType;
27  import javax.jcr.observation.Event;
28  import javax.jcr.observation.EventListener;
29  import javax.jcr.observation.ObservationManager;
30  import javax.jcr.query.Query;
31  import javax.jcr.query.QueryManager;
32  import javax.jcr.query.QueryResult;
33  
34  import org.exoplatform.container.xml.InitParams;
35  import org.exoplatform.services.cms.templates.TemplateService;
36  import org.exoplatform.services.cms.watch.WatchDocumentService;
37  import org.exoplatform.services.jcr.RepositoryService;
38  import org.exoplatform.services.jcr.config.RepositoryEntry;
39  import org.exoplatform.services.jcr.core.ManageableRepository;
40  import org.exoplatform.services.log.ExoLogger;
41  import org.exoplatform.services.log.Log;
42  import org.picocontainer.Startable;
43  
44  public class WatchDocumentServiceImpl implements WatchDocumentService, Startable {
45  
46    final public static String EXO_WATCHABLE_MIXIN = "exo:watchable" ;
47    final public static String EMAIL_WATCHERS_PROP = "exo:emailWatcher" ;
48    final public static String RSS_WATCHERS_PROP = "exo:rssWatcher" ;
49    final private static String WATCHABLE_MIXIN_QUERY = "//element(*,exo:watchable)" ;
50  
51    private RepositoryService repoService_ ;
52    private MessageConfig messageConfig_ ;
53    private TemplateService templateService_ ;
54    private static final Log LOG  = ExoLogger.getLogger(WatchDocumentServiceImpl.class.getName());
55  
56    /**
57     * Constructor Method
58     * @param params
59     * @param repoService
60     * @param templateService
61     */
62    public WatchDocumentServiceImpl(InitParams params,
63        RepositoryService repoService, TemplateService templateService) {
64      repoService_ = repoService ;
65      templateService_ = templateService ;
66    }
67    
68    /**
69     * {@inheritDoc}
70     */
71    public void initializeMessageConfig(MessageConfigPlugin msgConfigPlugin) {
72      messageConfig_ = msgConfigPlugin.getMessageConfig();
73    }
74  
75    /**
76     * {@inheritDoc}
77     */
78    public int getNotificationType(Node documentNode, String userName) throws Exception {
79      NodeType[] mixinTypes = documentNode.getMixinNodeTypes() ;
80      NodeType watchableMixin = null ;
81      if(mixinTypes.length>0) {
82        for(NodeType nodeType: mixinTypes) {
83          if(nodeType.getName().equalsIgnoreCase(EXO_WATCHABLE_MIXIN)) {
84            watchableMixin = nodeType ;
85            break ;
86          }
87        }
88      }
89      if(watchableMixin == null)  return -1 ;
90      boolean notifyByEmail = checkNotifyTypeOfWatcher(documentNode,userName,EMAIL_WATCHERS_PROP) ;
91      boolean notifyByRss = checkNotifyTypeOfWatcher(documentNode,userName,RSS_WATCHERS_PROP) ;
92      if( notifyByEmail && notifyByRss) return FULL_NOTIFICATION ;
93      if(notifyByEmail) return NOTIFICATION_BY_EMAIL ;
94      if(notifyByRss) return NOTIFICATION_BY_RSS ;
95      return -1 ;
96    }
97  
98    /**
99     * {@inheritDoc}
100    */
101   public void watchDocument(Node documentNode, String userName, int notifyType) throws Exception {
102     Session session = documentNode.getSession() ;
103     Value newWatcher = session.getValueFactory().createValue(userName) ;
104     if(!documentNode.isNodeType(EXO_WATCHABLE_MIXIN)) {
105       documentNode.addMixin(EXO_WATCHABLE_MIXIN) ;
106       if(notifyType == NOTIFICATION_BY_EMAIL) {
107         documentNode.setProperty(EMAIL_WATCHERS_PROP,new Value[] {newWatcher}) ;
108         documentNode.save() ;
109         session.save() ;
110         EmailNotifyListener listener = new EmailNotifyListener(documentNode) ;
111         observeNode(documentNode,listener) ;
112       }
113       session.save() ;
114     } else {
115       List<Value>  watcherList = new ArrayList<Value>() ;
116       if(notifyType == NOTIFICATION_BY_EMAIL) {
117         if(documentNode.hasProperty(EMAIL_WATCHERS_PROP)) {
118           for(Value watcher : documentNode.getProperty(EMAIL_WATCHERS_PROP).getValues()) {
119             watcherList.add(watcher) ;
120           }
121           watcherList.add(newWatcher) ;
122         }
123 
124         documentNode.setProperty(EMAIL_WATCHERS_PROP,watcherList.toArray(new Value[watcherList.size()])) ;
125         documentNode.save() ;
126       }
127       session.save() ;
128     }
129   }
130 
131   /**
132    * {@inheritDoc}
133    */
134   public void unwatchDocument(Node documentNode, String userName, int notificationType) throws Exception {
135     if(!documentNode.isNodeType(EXO_WATCHABLE_MIXIN)) return  ;
136     Session session = documentNode.getSession() ;
137     if(notificationType == NOTIFICATION_BY_EMAIL) {
138       Value[] watchers = documentNode.getProperty(EMAIL_WATCHERS_PROP).getValues() ;
139       List<Value> watcherList = new ArrayList<Value>() ;
140       for(Value watcher: watchers) {
141         if(!watcher.getString().equals(userName)) {
142           watcherList.add(watcher) ;
143         }
144       }
145       documentNode.setProperty(EMAIL_WATCHERS_PROP,watcherList.toArray(new Value[watcherList.size()])) ;
146     }
147     documentNode.save() ;
148     session.save() ;
149   }
150 
151   /**
152    * This method will observes the specification node by giving the following param : listener, node
153    * Its add an event listener to this node to observes anything that changes to this
154    * @param node              Specify the node to observe
155    * @param listener          The object of EventListener
156    * @see                     EventListener
157    * @see                     Node
158    * @throws Exception
159    */
160   private void observeNode(Node node, EventListener listener) throws Exception {
161     String workspace = node.getSession().getWorkspace().getName() ;
162     Session systemSession = repoService_.getCurrentRepository().getSystemSession(workspace) ;
163     List<String> list = getDocumentNodeTypes(node) ;
164     String[] observedNodeTypeNames = list.toArray(new String[list.size()]) ;
165     ObservationManager observationManager = systemSession.getWorkspace().getObservationManager() ;
166     observationManager.addEventListener(listener,Event.PROPERTY_CHANGED,
167         node.getPath(),true,null,observedNodeTypeNames,false) ;
168     systemSession.logout();
169   }
170 
171   /**
172    * This method will check notify type of watcher, userName is equal value of property with notification type
173    * @param documentNode    specify a node to watch
174    * @param userName        userName to watch a document
175    * @param notification    Notification Type
176    * @return boolean
177    * @throws Exception
178    */
179   private boolean checkNotifyTypeOfWatcher(Node documentNode, String userName,String notificationType) throws Exception {
180     if(documentNode.hasProperty(notificationType)) {
181       Value [] watchers = documentNode.getProperty(notificationType).getValues() ;
182       for(Value value: watchers) {
183         if(userName.equalsIgnoreCase(value.getString())) return true ;
184       }
185     }
186     return false ;
187   }
188 
189   /**
190    * This method will get all node types of node.
191    * @param node
192    * @return
193    * @throws Exception
194    */
195   private List<String> getDocumentNodeTypes(Node node) throws Exception {
196     List<String> nodeTypeNameList = new ArrayList<String>() ;
197     NodeType  primaryType = node.getPrimaryNodeType() ;
198     if(templateService_.isManagedNodeType(primaryType.getName())) {
199       nodeTypeNameList.add(primaryType.getName()) ;
200     }
201     for(NodeType nodeType: node.getMixinNodeTypes()) {
202       if(templateService_.isManagedNodeType(nodeType.getName())) {
203         nodeTypeNameList.add(nodeType.getName()) ;
204       }
205     }
206     return nodeTypeNameList ;
207   }
208 
209   /**
210    * This method will re-observer all nodes that have been ever observed with all repositories.
211    * @throws Exception
212    */
213   private void reInitObserver() throws Exception {
214     RepositoryEntry repo = repoService_.getCurrentRepository().getConfiguration();
215     ManageableRepository repository = repoService_.getCurrentRepository();
216     String[] workspaceNames = repository.getWorkspaceNames() ;
217     for(String workspace: workspaceNames) {
218       Session session = repository.getSystemSession(workspace) ;
219       QueryManager queryManager = null ;
220       try{
221         queryManager = session.getWorkspace().getQueryManager() ;
222       } catch (Exception e) {
223         if (LOG.isErrorEnabled()) {
224           LOG.error("Unexpected error", e);
225         }
226       }
227       if(queryManager == null) {
228         session.logout();
229         continue ;
230       }
231       try {
232         Query query = queryManager.createQuery(WATCHABLE_MIXIN_QUERY,Query.XPATH) ;
233         QueryResult queryResult = query.execute() ;
234         for(NodeIterator iter = queryResult.getNodes(); iter.hasNext(); ) {
235           Node observedNode = iter.nextNode() ;
236           EmailNotifyListener emailNotifyListener = new EmailNotifyListener(observedNode) ;
237           ObservationManager manager = session.getWorkspace().getObservationManager() ;
238           List<String> list = getDocumentNodeTypes(observedNode) ;
239           String[] observedNodeTypeNames = list.toArray(new String[list.size()]) ;
240           manager.addEventListener(emailNotifyListener,Event.PROPERTY_CHANGED,
241               observedNode.getPath(),true,null,observedNodeTypeNames,false) ;
242         }
243         session.logout();
244       } catch (Exception e) {
245         if (LOG.isWarnEnabled()) {
246           LOG.warn("==>>> Cannot init observer for node: "
247             +e.getLocalizedMessage() + " in '"+repo.getName()+"' repository");
248         }
249         if (LOG.isErrorEnabled()) {
250           LOG.error("Unexpected error", e);
251         }
252       }
253     }
254   }
255 
256   /**
257    * This method will get message configuration when a node is observing and there is some changes
258    * with it's properties.
259    * @return MessageCongig
260    */
261   protected MessageConfig getMessageConfig() { return messageConfig_ ; }
262 
263   /**
264    * using for re-observer
265    */
266   public void start() {
267     try {
268       reInitObserver() ;
269     }catch (Exception e) {
270       if (LOG.isWarnEnabled()) {
271         LOG.warn("==>>> Exeption when startd WatchDocumentSerice!!!!");
272       }
273     }
274   }
275 
276   /**
277    * {@inheritDoc}
278    */
279   public void stop() { }
280 }