001: /*
002: * Copyright 2007 The Kuali Foundation
003: *
004: * Licensed under the Educational Community License, Version 1.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.opensource.org/licenses/ecl1.php
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package edu.iu.uis.eden.messaging.serviceproxies;
017:
018: import java.io.Serializable;
019: import java.lang.reflect.Method;
020: import java.lang.reflect.Proxy;
021: import java.sql.Timestamp;
022: import java.util.Calendar;
023: import java.util.List;
024:
025: import org.apache.log4j.Logger;
026: import org.kuali.bus.services.KSBServiceLocator;
027: import org.kuali.rice.proxy.BaseInvocationHandler;
028: import org.kuali.rice.proxy.TargetedInvocationHandler;
029: import org.kuali.rice.resourceloader.ContextClassLoaderProxy;
030: import org.kuali.rice.util.ClassLoaderUtils;
031: import org.quartz.JobDataMap;
032: import org.quartz.JobDetail;
033: import org.quartz.Scheduler;
034: import org.quartz.SchedulerException;
035: import org.quartz.SimpleTrigger;
036: import org.quartz.Trigger;
037:
038: import edu.iu.uis.eden.messaging.AsynchronousCall;
039: import edu.iu.uis.eden.messaging.PersistedMessage;
040: import edu.iu.uis.eden.messaging.RemotedServiceHolder;
041: import edu.iu.uis.eden.messaging.ServiceInfo;
042: import edu.iu.uis.eden.messaging.quartz.MessageServiceExecutorJob;
043: import edu.iu.uis.eden.messaging.quartz.MessageServiceExecutorJobListener;
044:
045: /**
046: * A proxy which schedules a service to be executed asynchronously after some delay period.
047: *
048: * @author Kuali Rice Team (kuali-rice@googlegroups.com)
049: */
050: public class DelayedAsynchronousServiceCallProxy extends
051: BaseInvocationHandler implements TargetedInvocationHandler {
052:
053: private static final Logger LOG = Logger
054: .getLogger(DelayedAsynchronousServiceCallProxy.class);
055:
056: List<RemotedServiceHolder> serviceDefs;
057: private Serializable context;
058: private String value1;
059: private String value2;
060: private long delayMilliseconds;
061:
062: protected DelayedAsynchronousServiceCallProxy(
063: List<RemotedServiceHolder> serviceDefs,
064: Serializable context, String value1, String value2,
065: long delayMilliseconds) {
066: this .serviceDefs = serviceDefs;
067: this .context = context;
068: this .value1 = value1;
069: this .value2 = value2;
070: this .delayMilliseconds = delayMilliseconds;
071: }
072:
073: public static Object createInstance(
074: List<RemotedServiceHolder> serviceDefs,
075: Serializable context, String value1, String value2,
076: long delayMilliseconds) {
077: if (serviceDefs == null || serviceDefs.isEmpty()) {
078: throw new RuntimeException(
079: "Cannot create service proxy, no service(s) passed in.");
080: }
081: return Proxy.newProxyInstance(ClassLoaderUtils
082: .getDefaultClassLoader(), ContextClassLoaderProxy
083: .getInterfacesToProxyIncludeSpring(serviceDefs.get(0)
084: .getService()),
085: new DelayedAsynchronousServiceCallProxy(serviceDefs,
086: context, value1, value2, delayMilliseconds));
087: }
088:
089: @Override
090: protected Object invokeInternal(Object proxy, Method method,
091: Object[] arguments) throws Throwable {
092: // there are multiple service calls to make in the case of topics.
093: AsynchronousCall methodCall = null;
094: PersistedMessage message = null;
095: synchronized (this ) {
096: // consider moving all this topic invocation stuff to the service
097: // invoker for speed reasons
098: for (RemotedServiceHolder remotedServiceHolder : this .serviceDefs) {
099: ServiceInfo serviceInfo = remotedServiceHolder
100: .getServiceInfo();
101: methodCall = new AsynchronousCall(method
102: .getParameterTypes(), arguments, serviceInfo,
103: method.getName(), null, this .context);
104: message = KSBServiceLocator.getRouteQueueService()
105: .getMessage(serviceInfo, methodCall);
106: message.setValue1(this .value1);
107: message.setValue2(this .value2);
108: Calendar now = Calendar.getInstance();
109: now.add(Calendar.MILLISECOND, (int) delayMilliseconds);
110: message.setQueueDate(new Timestamp(now
111: .getTimeInMillis()));
112: scheduleMessage(message);
113: // only do one iteration if this is a queue. The load balancing
114: // will be handled when the service is
115: // fetched by the MessageServiceInvoker through the GRL (and
116: // then through the RemoteResourceServiceLocatorImpl)
117: if (serviceInfo.getServiceDefinition().getQueue()) {
118: break;
119: }
120: }
121: }
122: return null;
123: }
124:
125: protected void scheduleMessage(PersistedMessage message)
126: throws SchedulerException {
127: LOG
128: .debug("Scheduling execution of a delayed asynchronous message.");
129: Scheduler scheduler = KSBServiceLocator.getScheduler();
130: JobDataMap jobData = new JobDataMap();
131: jobData.put(MessageServiceExecutorJob.MESSAGE_KEY, message);
132: JobDetail jobDetail = new JobDetail(
133: "Delayed_Asynchronous_Call-" + Math.random(),
134: "Delayed_Asynchronous_Call",
135: MessageServiceExecutorJob.class);
136: jobDetail.setJobDataMap(jobData);
137: jobDetail
138: .addJobListener(MessageServiceExecutorJobListener.NAME);
139: Trigger trigger = new SimpleTrigger(
140: "Delayed_Asynchronous_Call_Trigger-" + Math.random(),
141: "Delayed_Asynchronous_Call", message.getQueueDate());
142: trigger.setJobDataMap(jobData);//1.6 bug required or derby will choke
143: scheduler.scheduleJob(jobDetail, trigger);
144: }
145:
146: /**
147: * Returns the List<RemotedServiceHolder> of asynchronous services which will be invoked by calls to this proxy.
148: * This is a List because, in the case of Topics, there can be more than one service invoked.
149: */
150: public Object getTarget() {
151: return this .serviceDefs;
152: }
153:
154: public List<RemotedServiceHolder> getServiceDefs() {
155: return this .serviceDefs;
156: }
157:
158: public void setServiceDefs(List<RemotedServiceHolder> serviceDefs) {
159: this.serviceDefs = serviceDefs;
160: }
161:
162: }
|