/* * Copyright (C) 2003-2007 eXo Platform SAS. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation; either version 3 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see<http://www.gnu.org/licenses/>. */ package org.exoplatform.services.cms.watch.impl; import java.util.ArrayList; import java.util.List; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.nodetype.NodeType; import javax.jcr.observation.Event; import javax.jcr.observation.EventListener; import javax.jcr.observation.ObservationManager; import javax.jcr.query.Query; import javax.jcr.query.QueryManager; import javax.jcr.query.QueryResult; import org.exoplatform.container.xml.InitParams; import org.exoplatform.services.cms.templates.TemplateService; import org.exoplatform.services.cms.watch.WatchDocumentService; import org.exoplatform.services.jcr.RepositoryService; import org.exoplatform.services.jcr.config.RepositoryEntry; import org.exoplatform.services.jcr.core.ManageableRepository; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.picocontainer.Startable; public class WatchDocumentServiceImpl implements WatchDocumentService, Startable { final public static String EXO_WATCHABLE_MIXIN = "exo:watchable" ; final public static String EMAIL_WATCHERS_PROP = "exo:emailWatcher" ; final public static String RSS_WATCHERS_PROP = "exo:rssWatcher" ; final private static String WATCHABLE_MIXIN_QUERY = "//element(*,exo:watchable)" ; private RepositoryService repoService_ ; private MessageConfig messageConfig_ ; private TemplateService templateService_ ; private static final Log LOG = ExoLogger.getLogger(WatchDocumentServiceImpl.class.getName()); /** * Constructor Method * @param params * @param repoService * @param templateService */ public WatchDocumentServiceImpl(InitParams params, RepositoryService repoService, TemplateService templateService) { repoService_ = repoService ; templateService_ = templateService ; } /** * {@inheritDoc} */ public void initializeMessageConfig(MessageConfigPlugin msgConfigPlugin) { messageConfig_ = msgConfigPlugin.getMessageConfig(); } /** * {@inheritDoc} */ public int getNotificationType(Node documentNode, String userName) throws Exception { NodeType[] mixinTypes = documentNode.getMixinNodeTypes() ; NodeType watchableMixin = null ; if(mixinTypes.length>0) { for(NodeType nodeType: mixinTypes) { if(nodeType.getName().equalsIgnoreCase(EXO_WATCHABLE_MIXIN)) { watchableMixin = nodeType ; break ; } } } if(watchableMixin == null) return -1 ; boolean notifyByEmail = checkNotifyTypeOfWatcher(documentNode,userName,EMAIL_WATCHERS_PROP) ; boolean notifyByRss = checkNotifyTypeOfWatcher(documentNode,userName,RSS_WATCHERS_PROP) ; if( notifyByEmail && notifyByRss) return FULL_NOTIFICATION ; if(notifyByEmail) return NOTIFICATION_BY_EMAIL ; if(notifyByRss) return NOTIFICATION_BY_RSS ; return -1 ; } /** * {@inheritDoc} */ public void watchDocument(Node documentNode, String userName, int notifyType) throws Exception { Session session = documentNode.getSession() ; Value newWatcher = session.getValueFactory().createValue(userName) ; if(!documentNode.isNodeType(EXO_WATCHABLE_MIXIN)) { documentNode.addMixin(EXO_WATCHABLE_MIXIN) ; if(notifyType == NOTIFICATION_BY_EMAIL) { documentNode.setProperty(EMAIL_WATCHERS_PROP,new Value[] {newWatcher}) ; documentNode.save() ; session.save() ; EmailNotifyListener listener = new EmailNotifyListener(documentNode) ; observeNode(documentNode,listener) ; } session.save() ; } else { List<Value> watcherList = new ArrayList<Value>() ; if(notifyType == NOTIFICATION_BY_EMAIL) { if(documentNode.hasProperty(EMAIL_WATCHERS_PROP)) { for(Value watcher : documentNode.getProperty(EMAIL_WATCHERS_PROP).getValues()) { watcherList.add(watcher) ; } watcherList.add(newWatcher) ; } documentNode.setProperty(EMAIL_WATCHERS_PROP,watcherList.toArray(new Value[watcherList.size()])) ; documentNode.save() ; } session.save() ; } } /** * {@inheritDoc} */ public void unwatchDocument(Node documentNode, String userName, int notificationType) throws Exception { if(!documentNode.isNodeType(EXO_WATCHABLE_MIXIN)) return ; Session session = documentNode.getSession() ; if(notificationType == NOTIFICATION_BY_EMAIL) { Value[] watchers = documentNode.getProperty(EMAIL_WATCHERS_PROP).getValues() ; List<Value> watcherList = new ArrayList<Value>() ; for(Value watcher: watchers) { if(!watcher.getString().equals(userName)) { watcherList.add(watcher) ; } } documentNode.setProperty(EMAIL_WATCHERS_PROP,watcherList.toArray(new Value[watcherList.size()])) ; } documentNode.save() ; session.save() ; } /** * This method will observes the specification node by giving the following param : listener, node * Its add an event listener to this node to observes anything that changes to this * @param node Specify the node to observe * @param listener The object of EventListener * @see EventListener * @see Node * @throws Exception */ private void observeNode(Node node, EventListener listener) throws Exception { String workspace = node.getSession().getWorkspace().getName() ; Session systemSession = repoService_.getCurrentRepository().getSystemSession(workspace) ; List<String> list = getDocumentNodeTypes(node) ; String[] observedNodeTypeNames = list.toArray(new String[list.size()]) ; ObservationManager observationManager = systemSession.getWorkspace().getObservationManager() ; observationManager.addEventListener(listener,Event.PROPERTY_CHANGED, node.getPath(),true,null,observedNodeTypeNames,false) ; systemSession.logout(); } /** * This method will check notify type of watcher, userName is equal value of property with notification type * @param documentNode specify a node to watch * @param userName userName to watch a document * @param notification Notification Type * @return boolean * @throws Exception */ private boolean checkNotifyTypeOfWatcher(Node documentNode, String userName,String notificationType) throws Exception { if(documentNode.hasProperty(notificationType)) { Value [] watchers = documentNode.getProperty(notificationType).getValues() ; for(Value value: watchers) { if(userName.equalsIgnoreCase(value.getString())) return true ; } } return false ; } /** * This method will get all node types of node. * @param node * @return * @throws Exception */ private List<String> getDocumentNodeTypes(Node node) throws Exception { List<String> nodeTypeNameList = new ArrayList<String>() ; NodeType primaryType = node.getPrimaryNodeType() ; if(templateService_.isManagedNodeType(primaryType.getName())) { nodeTypeNameList.add(primaryType.getName()) ; } for(NodeType nodeType: node.getMixinNodeTypes()) { if(templateService_.isManagedNodeType(nodeType.getName())) { nodeTypeNameList.add(nodeType.getName()) ; } } return nodeTypeNameList ; } /** * This method will re-observer all nodes that have been ever observed with all repositories. * @throws Exception */ private void reInitObserver() throws Exception { RepositoryEntry repo = repoService_.getCurrentRepository().getConfiguration(); ManageableRepository repository = repoService_.getCurrentRepository(); String[] workspaceNames = repository.getWorkspaceNames() ; for(String workspace: workspaceNames) { Session session = repository.getSystemSession(workspace) ; QueryManager queryManager = null ; try{ queryManager = session.getWorkspace().getQueryManager() ; } catch (Exception e) { if (LOG.isErrorEnabled()) { LOG.error("Unexpected error", e); } } if(queryManager == null) { session.logout(); continue ; } try { Query query = queryManager.createQuery(WATCHABLE_MIXIN_QUERY,Query.XPATH) ; QueryResult queryResult = query.execute() ; for(NodeIterator iter = queryResult.getNodes(); iter.hasNext(); ) { Node observedNode = iter.nextNode() ; EmailNotifyListener emailNotifyListener = new EmailNotifyListener(observedNode) ; ObservationManager manager = session.getWorkspace().getObservationManager() ; List<String> list = getDocumentNodeTypes(observedNode) ; String[] observedNodeTypeNames = list.toArray(new String[list.size()]) ; manager.addEventListener(emailNotifyListener,Event.PROPERTY_CHANGED, observedNode.getPath(),true,null,observedNodeTypeNames,false) ; } session.logout(); } catch (Exception e) { if (LOG.isWarnEnabled()) { LOG.warn("==>>> Cannot init observer for node: " +e.getLocalizedMessage() + " in '"+repo.getName()+"' repository"); } if (LOG.isErrorEnabled()) { LOG.error("Unexpected error", e); } } } } /** * This method will get message configuration when a node is observing and there is some changes * with it's properties. * @return MessageCongig */ protected MessageConfig getMessageConfig() { return messageConfig_ ; } /** * using for re-observer */ public void start() { try { reInitObserver() ; }catch (Exception e) { if (LOG.isWarnEnabled()) { LOG.warn("==>>> Exeption when startd WatchDocumentSerice!!!!"); } } } /** * {@inheritDoc} */ public void stop() { } }