001/** 002 * Copyright 2005-2016 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 */ 016package org.kuali.rice.ksb.messaging.serviceproxies; 017 018import org.apache.commons.collections.CollectionUtils; 019import org.apache.log4j.Logger; 020import org.kuali.rice.core.api.exception.RiceRuntimeException; 021import org.kuali.rice.core.api.util.ClassLoaderUtils; 022import org.kuali.rice.core.api.util.ContextClassLoaderProxy; 023import org.kuali.rice.core.api.util.reflect.BaseInvocationHandler; 024import org.kuali.rice.core.api.util.reflect.TargetedInvocationHandler; 025import org.kuali.rice.ksb.api.bus.Endpoint; 026import org.kuali.rice.ksb.api.bus.ServiceConfiguration; 027import org.kuali.rice.ksb.api.messaging.AsynchronousCall; 028import org.kuali.rice.ksb.api.messaging.AsynchronousCallback; 029import org.kuali.rice.ksb.messaging.PersistedMessageBO; 030import org.kuali.rice.ksb.service.KSBServiceLocator; 031import org.kuali.rice.ksb.util.KSBConstants; 032 033import java.io.Serializable; 034import java.lang.reflect.Method; 035import java.lang.reflect.Proxy; 036import 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 */ 044public 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}