/*
* Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
package com.arsdigita.notification;
import com.arsdigita.kernel.Kernel;
import com.arsdigita.kernel.KernelExcursion;
import com.arsdigita.kernel.Group;
import com.arsdigita.kernel.Party;
import com.arsdigita.kernel.User;
import com.arsdigita.kernel.UserCollection;
import com.arsdigita.domain.DataObjectNotFoundException;
import com.arsdigita.domain.DomainObjectFactory;
import com.arsdigita.persistence.DataQuery;
import com.arsdigita.persistence.OID;
import com.arsdigita.persistence.Session;
import com.arsdigita.persistence.SessionManager;
import com.arsdigita.util.UncheckedWrapperException;
import java.math.BigDecimal;
import java.util.TimerTask;
import org.apache.log4j.Logger;
/**
* The RequestManager is a child of TimerTask. It is scheduled for periodic
* execution in the Initializer. RequestManager sweeps through the list of
* notification requests and queues them for sending. Once a request is in
* the queue, its status is updated each time together and any necessary
* clean-up work is performed.
*
* @author David Dao
* @version $Id: RequestManager.java 287 2005-02-22 00:29:02Z sskracic $
*/
class RequestManager extends TimerTask {
private static final Logger s_log =
Logger.getLogger(RequestManager.class.getName());
// Persistence key attributes
private static final String REQUEST_ID = "requestID";
private static final String PARTY_TO = "partyTo";
private static final String NOTIFICATION_IN_QUEUE = "queued";
// PDL Queries
private static final String UPDATE_NOTIFICATION_SUCCESSFUL =
"com.arsdigita.notification.UpdateNotificationSuccessful";
private static final String UPDATE_NOTIFICATION_FAILURE =
"com.arsdigita.notification.UpdateNotificationFailure";
private static final String UPDATE_NOTIFICATION_PARTIAL_FAILURE =
"com.arsdigita.notification.UpdateNotificationPartialFailure";
private static final String GET_PENDING_NOTIFCATIONS =
"com.arsdigita.notification.GetPendingNotifications";
private static final String DELETE_NOTIFICATION_QUEUED =
"com.arsdigita.notification.DeleteNotificationQueued";
private static final String GET_COMPLETE_NOTIFICATIONS =
"com.arsdigita.notification.GetCompleteNotifications";
/**
* Implements the run method of TimerTask. This is the execution block each
* time the RequestManager runs. The following actions are performed:
*
* - Updates the status of requests, identifying which succeeded or failed
* since the method was last run
*
- Sweeps processed requests from the queue
*
- Queues new pending requests
*
*/
public void run() {
KernelExcursion rootExcursion = new KernelExcursion() {
protected void excurse() {
setEffectiveParty(Kernel.getSystemParty());
s_log.info("processing current requests.");
long time = System.currentTimeMillis();
/**
* Check and update status of requests that have already been queued.
*/
boolean committedTxn = false;
Session session = SessionManager.getSession();
session.getTransactionContext().beginTxn();
try {
session.retrieveDataOperation(UPDATE_NOTIFICATION_SUCCESSFUL).execute();
session.retrieveDataOperation(UPDATE_NOTIFICATION_FAILURE).execute();
/**
* NOTE: I need to rewrite this query. Currently this query is working
* correctly if it executes after UPDATE_NOTIFICATION_SUCCESSFUL and
* UPDATE_NOTIFICATION_FAILURE.
*/
session.retrieveDataOperation(UPDATE_NOTIFICATION_PARTIAL_FAILURE).execute();
// delete from nt_queue where status in ('sent', failed, partial-failure)
session.retrieveDataOperation(DELETE_NOTIFICATION_QUEUED).execute();
// Iterate through a list of notifications and invoke delete on each
// object.
// Why I am not using one query to do the delete?
// Because there is no DELETE CASCADE in the datamodel for nt_requests
// table.
DataQuery qry = session.retrieveQuery(GET_COMPLETE_NOTIFICATIONS);
while (qry.next()) {
try {
OID oid = new OID(Notification.BASE_DATA_OBJECT_TYPE, qry.get(REQUEST_ID));
Notification n = new Notification(oid);
n.delete();
} catch (DataObjectNotFoundException e) {
s_log.error("Retrieve complete notification", e);
// skip this notification.
}
}
session.getTransactionContext().commitTxn();
committedTxn = true;
} finally {
if (!committedTxn) {
session.getTransactionContext().abortTxn();
}
}
/**
* Tranfer pending requests to the outbound queue.
*/
session.getTransactionContext().beginTxn();
committedTxn = false;
try {
DataQuery query = session.retrieveQuery(GET_PENDING_NOTIFCATIONS);
while (query.next()) {
BigDecimal requestID = (BigDecimal) query.get(REQUEST_ID);
BigDecimal partyTo = (BigDecimal) query.get(PARTY_TO);
try {
OID oid = new OID(Party.BASE_DATA_OBJECT_TYPE, partyTo);
Party party = (Party) DomainObjectFactory.newInstance(oid);
Notification notification = new Notification(new OID(Notification.BASE_DATA_OBJECT_TYPE, requestID));
notification.setStatus(NOTIFICATION_IN_QUEUE);
notification.save();
// Add notification to queue
Boolean isGroupExpand = notification.getExpandGroup();
boolean expand = true; // Default value for group expand
if (isGroupExpand != null)
expand = isGroupExpand.booleanValue();
if ((party instanceof User) || (!expand)) {
QueueItem queued = new QueueItem(notification, party);
queued.save();
} else if (party instanceof Group) {
// Expand group.
Group group = (Group) party;
UserCollection userCollection = group.getAllMemberUsers();
while (userCollection.next()) {
User user = userCollection.getUser();
QueueItem queued = new QueueItem(notification, user);
queued.save();
}
}
} catch (Exception e) {
s_log.warn("RequestManager", e);
e.printStackTrace();
}
}
session.getTransactionContext().commitTxn();
committedTxn = true;
} finally {
if (!committedTxn) {
session.getTransactionContext().abortTxn();
}
}
time = System.currentTimeMillis() - time;
s_log.info("RequestManager executed in " + time + " ms.");
}};
try {
rootExcursion.run();
} catch (Throwable t) {
s_log.error("Unexpected error occured. RequestManager disabled", t);
throw new UncheckedWrapperException(t);
}
}
}