/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.watchlist.internal.job;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xwiki.watchlist.internal.DefaultWatchListNotifier;
import org.xwiki.watchlist.internal.WatchListEventMatcher;
import org.xwiki.watchlist.internal.api.WatchList;
import org.xwiki.watchlist.internal.api.WatchListEvent;
import org.xwiki.watchlist.internal.documents.WatchListJobClassDocumentInitializer;
import org.xwiki.watchlist.internal.notification.WatchListEventMimeMessageFactory;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.BaseObject;
import com.xpn.xwiki.plugin.scheduler.AbstractJob;
import com.xpn.xwiki.web.Utils;
/**
* WatchList abstract implementation of Quartz's Job.
*
* @version $Id: 2e1ba48a94e19157a871e1b9b114b80026cf5a54 $
*/
public class WatchListJob extends AbstractJob implements Job
{
/**
* Wiki page which contains the default watchlist email template.
*/
public static final String DEFAULT_EMAIL_TEMPLATE = "XWiki.WatchListMessage";
/**
* Logger.
*/
private static final Logger LOGGER = LoggerFactory.getLogger(WatchListJob.class);
/**
* Scheduler Job XObject.
*/
private BaseObject schedulerJobObject;
/**
* Watchlist Job XObject.
*/
private BaseObject watchListJobObject;
/**
* Caller component.
*/
private WatchList watchlist;
/**
* Sets objects required by the Job : XWiki, XWikiContext, WatchListPlugin, etc.
*
* @param jobContext Context of the request
* @throws Exception when the init of components fails
*/
public void init(JobExecutionContext jobContext) throws Exception
{
JobDataMap data = jobContext.getJobDetail().getJobDataMap();
this.watchlist = Utils.getComponent(WatchList.class);
this.schedulerJobObject = (BaseObject) data.get("xjob");
this.watchListJobObject =
getXWikiContext().getWiki().getDocument(this.schedulerJobObject.getDocumentReference(), getXWikiContext())
.getXObject(WatchListJobClassDocumentInitializer.DOCUMENT_REFERENCE);
}
/**
* @return ID of the job
*/
public String getId()
{
String className = this.getClass().getName();
return className.substring(className.lastIndexOf(".") + 1);
}
/**
* @return the previous job fire time
*/
private Date getPreviousFireTime()
{
return this.watchListJobObject.getDateValue(WatchListJobClassDocumentInitializer.LAST_FIRE_TIME_FIELD);
}
/**
* Save the date of the execution in the watchlist job object.
*
* @throws XWikiException if the job document can't be retrieved or if the save action fails
*/
private void setPreviousFireTime() throws XWikiException
{
XWikiDocument doc =
getXWikiContext().getWiki().getDocument(this.watchListJobObject.getDocumentReference(), getXWikiContext());
this.watchListJobObject.setDateValue(WatchListJobClassDocumentInitializer.LAST_FIRE_TIME_FIELD, new Date());
// Prevent version changes
doc.setMetaDataDirty(false);
doc.setContentDirty(false);
getXWikiContext().getWiki().saveDocument(doc, "Updated last fire time", true, getXWikiContext());
}
/**
* Retrieves all the XWiki.XWikiUsers who have requested to be notified by changes, i.e. who have an Object of class
* WATCHLIST_CLASS attached AND who have chosen the current job for their notifications.
*
* @return a collection of document names pointing to the XWikiUsers wishing to get notified.
*/
private Collection<String> getSubscribers()
{
return this.watchlist.getStore().getSubscribers(this.schedulerJobObject.getName());
}
/**
* @return true if this job has subscribers, false otherwise
*/
private boolean hasSubscribers()
{
Collection<String> subscribers = getSubscribers();
return !subscribers.isEmpty();
}
/**
* Method called from the scheduler.
*
* @param jobContext Context of the request
* @throws JobExecutionException if the job execution fails.
*/
@Override
public void executeJob(JobExecutionContext jobContext) throws JobExecutionException
{
try {
init(jobContext);
if (this.watchListJobObject == null) {
return;
}
Collection<String> subscribers = getSubscribers();
// Stop here if nobody is interested.
if (!hasSubscribers()) {
return;
}
// Determine what happened since the last execution for everybody.
Date previousFireTime = getPreviousFireTime();
WatchListEventMatcher eventMatcher = Utils.getComponent(WatchListEventMatcher.class);
List<WatchListEvent> events = eventMatcher.getEventsSince(previousFireTime);
setPreviousFireTime();
// Stop here if nothing happened in the meantime.
if (events.size() == 0) {
return;
}
// Notify all the interested subscribers of the events that occurred.
// When processing the events, a subscriber will only be notified of events that interest him.
Map<String, Object> notificationData = new HashMap<>();
notificationData.put(DefaultWatchListNotifier.PREVIOUS_FIRE_TIME_VARIABLE, previousFireTime);
String mailTemplate =
this.watchListJobObject.getStringValue(WatchListJobClassDocumentInitializer.TEMPLATE_FIELD);
notificationData.put(WatchListEventMimeMessageFactory.TEMPLATE_PARAMETER, mailTemplate);
// Send the notification for processing.
this.watchlist.getNotifier().sendNotification(subscribers, events, notificationData);
} catch (Exception e) {
// We're in a job, we don't throw exceptions
LOGGER.error("Exception while running job", e);
}
}
}