001 /**
002 * Copyright 2005-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.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/ecl2.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 org.kuali.rice.ksb.messaging.serviceproxies;
017
018 import org.apache.commons.collections.CollectionUtils;
019 import org.apache.log4j.Logger;
020 import org.kuali.rice.core.api.exception.RiceRuntimeException;
021 import org.kuali.rice.core.api.util.ClassLoaderUtils;
022 import org.kuali.rice.core.api.util.ContextClassLoaderProxy;
023 import org.kuali.rice.core.api.util.reflect.BaseInvocationHandler;
024 import org.kuali.rice.core.api.util.reflect.TargetedInvocationHandler;
025 import org.kuali.rice.ksb.api.bus.Endpoint;
026 import org.kuali.rice.ksb.api.bus.ServiceConfiguration;
027 import org.kuali.rice.ksb.api.messaging.AsynchronousCall;
028 import org.kuali.rice.ksb.api.messaging.AsynchronousCallback;
029 import org.kuali.rice.ksb.messaging.PersistedMessageBO;
030 import org.kuali.rice.ksb.service.KSBServiceLocator;
031 import org.kuali.rice.ksb.util.KSBConstants;
032
033 import java.io.Serializable;
034 import java.lang.reflect.Method;
035 import java.lang.reflect.Proxy;
036 import java.util.List;
037
038 /**
039 * Standard default proxy used to call services asynchronously. Persists the method call to the db so call is never
040 * lost and only sent when transaction is committed.
041 *
042 * @author Kuali Rice Team (rice.collab@kuali.org)
043 */
044 public class AsynchronousServiceCallProxy extends BaseInvocationHandler implements TargetedInvocationHandler {
045
046 private static final Logger LOG = Logger.getLogger(AsynchronousServiceCallProxy.class);
047
048 private AsynchronousCallback callback;
049
050 private List<Endpoint> endpoints;
051
052 private Serializable context;
053
054 private String value1;
055
056 private String value2;
057
058 protected AsynchronousServiceCallProxy(List<Endpoint> endpoints, AsynchronousCallback callback,
059 Serializable context, String value1, String value2) {
060 this.endpoints = endpoints;
061 this.callback = callback;
062 this.context = context;
063 this.value1 = value1;
064 this.value2 = value2;
065 }
066
067 public static Object createInstance(List<Endpoint> endpoints, AsynchronousCallback callback, Serializable context,
068 String value1, String value2) {
069 if (endpoints == null || endpoints.isEmpty()) {
070 throw new RuntimeException("Cannot create service proxy, no service(s) passed in.");
071 }
072 try {
073 return Proxy.newProxyInstance(ClassLoaderUtils.getDefaultClassLoader(),
074 ContextClassLoaderProxy.getInterfacesToProxy(endpoints.get(0).getService()),
075 new AsynchronousServiceCallProxy(endpoints, callback, context, value1, value2));
076 } catch (Exception e) {
077 throw new RiceRuntimeException(e);
078 }
079 }
080
081 @Override
082 protected Object invokeInternal(Object proxy, Method method, Object[] arguments) throws Throwable {
083
084 if (LOG.isDebugEnabled()) {
085 LOG.debug("creating messages for method invocation: " + method.getName());
086 }
087 // there are multiple service calls to make in the case of topics.
088 AsynchronousCall methodCall = null;
089 PersistedMessageBO message = null;
090 synchronized (this) {
091 // consider moving all this topic invocation stuff to the service
092 // invoker for speed reasons
093 for (Endpoint endpoint : this.endpoints) {
094 ServiceConfiguration serviceConfiguration = endpoint.getServiceConfiguration();
095 methodCall = new AsynchronousCall(method.getParameterTypes(), arguments, serviceConfiguration,
096 method.getName(), this.callback, this.context);
097 message = PersistedMessageBO.buildMessage(serviceConfiguration, methodCall);
098 message.setValue1(this.value1);
099 message.setValue2(this.value2);
100 saveMessage(message);
101 executeMessage(message);
102 // only do one iteration if this is a queue. The load balancing
103 // will be handled when the service is
104 // fetched by the MessageServiceInvoker through the GRL (and
105 // then through the RemoteResourceServiceLocatorImpl)
106 if (serviceConfiguration.isQueue()) {
107 break;
108 }
109 }
110 }
111 if (LOG.isDebugEnabled()) {
112 LOG.debug("finished creating messages for method invocation: " + method.getName());
113 }
114 return null;
115 }
116
117 @Override
118 protected String proxyToString(Object proxy) {
119 StringBuilder builder = new StringBuilder();
120 builder.append("Service call proxy (" + getClass().getName() + ") - for endpoints with service name: ");
121 if (CollectionUtils.isNotEmpty(this.endpoints)) {
122 builder.append(this.endpoints.get(0).getServiceConfiguration().getServiceName().toString());
123 } else {
124 builder.append("<< no endpoints on this proxy!!! >>");
125 }
126 return builder.toString();
127 }
128
129 protected void saveMessage(PersistedMessageBO message) {
130 message.setQueueStatus(KSBConstants.ROUTE_QUEUE_ROUTING);
131 KSBServiceLocator.getMessageQueueService().save(message);
132 }
133
134 protected void executeMessage(PersistedMessageBO message) throws Exception {
135 MessageSender.sendMessage(message);
136 }
137
138 /**
139 * Returns the List<RemotedServiceHolder> of asynchronous services which will be invoked by calls to this proxy.
140 * This is a List because, in the case of Topics, there can be more than one service invoked.
141 */
142 public Object getTarget() {
143 return this.endpoints;
144 }
145
146 public AsynchronousCallback getCallback() {
147 return this.callback;
148 }
149
150 public void setCallback(AsynchronousCallback callback) {
151 this.callback = callback;
152 }
153
154 }