1 /*
2 * Copyright (c) 1998-2003 The Jgroup Team.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
16 *
17 */
18 package jgroup.relacs.gmi;
19
20 import java.io.IOException;
21 import java.io.ObjectInputStream;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Proxy;
24 import java.rmi.Remote;
25 import java.util.Collection;
26 import java.util.concurrent.locks.Condition;
27 import java.util.concurrent.locks.Lock;
28 import java.util.concurrent.locks.ReentrantLock;
29
30 import jgroup.core.ExternalGMIListener;
31 import jgroup.core.GroupManager;
32 import jgroup.core.View;
33 import net.jini.jeri.InboundRequest;
34 import net.jini.jeri.InvocationDispatcher;
35
36 import org.apache.log4j.Logger;
37
38 /**
39 * Dispatches external invocations to the local group member by unmarshalling
40 * the invocation method and parameters.
41 *
42 * @author Tor Arve Stangeland
43 * @author Hein Meling
44 */
45 public class GroupInvocationDispatcher
46 implements InvocationDispatcher
47 {
48
49 ////////////////////////////////////////////////////////////////////////////////////////////
50 // Logger
51 ////////////////////////////////////////////////////////////////////////////////////////////
52
53 /** Obtain logger for this class */
54 private static final Logger log = Logger.getLogger(GroupInvocationDispatcher.class);
55
56
57 ////////////////////////////////////////////////////////////////////////////////////////////
58 // Fields
59 ////////////////////////////////////////////////////////////////////////////////////////////
60
61 /** Method table for converting hash to method */
62 private final MethodTable methodTable;
63
64 /** The group manager */
65 private final GroupManager groupManager;
66
67 /** The server-side view */
68 private View serverView;
69
70 /**
71 * True if invocation requests should be blocked awaiting a new view.
72 * Initially, we do not accept any invocations until we have installed a view.
73 */
74 private volatile boolean blockInvocation = true;
75
76 /** Lock and condition used to enable/disable method dispatching */
77 private final Lock lock = new ReentrantLock();
78 private final Condition notDispatching = lock.newCondition();
79
80
81 ////////////////////////////////////////////////////////////////////////////////////////////
82 // Constructor
83 ////////////////////////////////////////////////////////////////////////////////////////////
84
85 /**
86 * Constructs a new invocation dispatcher, using the given table of
87 * available server side EGMI methods.
88 */
89 public GroupInvocationDispatcher(GroupManager gm, MethodTable methodTable)
90 {
91 this.groupManager = gm;
92 this.methodTable = methodTable;
93 }
94
95
96 ////////////////////////////////////////////////////////////////////////////////////////////
97 // Public methods
98 ////////////////////////////////////////////////////////////////////////////////////////////
99
100 /**
101 * Invokes serialized request on object.
102 *
103 * @param stream InputStream to read invocation from
104 * @return an <code>InvocationResult</code> instance containing the
105 * invocation result, or null if the execution failed in some way.
106 */
107 public InvocationResult dispatch(ObjectInputStream in)
108 {
109 // Unmarshall the method hash
110 long hash;
111 long clientView;
112 try {
113 clientView = in.readLong();
114 hash = in.readLong();
115 } catch (IOException ioex) {
116 /* Something went wrong when reading from stream */
117 log.warn("Could not read method hash from stream, aborting request!", ioex);
118 return null;
119 }
120
121 // Lookup the method for the unmarshalled hash
122 MethodDetails m = methodTable.get(hash);
123 Method method = m.getMethod();
124 if (m == null) {
125 log.warn("The provided hash does not correspond to a known server-side method: " + hash);
126 return null;
127 }
128
129 // Set up the params array to hold the unmarshalled method parameters
130 Class[] paramTypes = method.getParameterTypes();
131 Object[] params = new Object[paramTypes.length];
132 try {
133 // Unmarshall the method parameters
134 for (int i = 0; i < params.length; ++i) {
135 params[i] = in.readObject();
136 if (params[i] instanceof ExternalGMIListener) {
137 /*
138 * If the parameter is a egmi remote reference ensure
139 * to set the group manager reference on the handler.
140 */
141 GroupInvocationHandler handler =
142 (GroupInvocationHandler) Proxy.getInvocationHandler(params[i]);
143 handler.setGroupManager(groupManager);
144 }
145 }
146 } catch (ClassNotFoundException cnfex) {
147 log.warn("ClassNotFoundException caught when unmarshalling arguments", cnfex);
148 return null;
149 } catch (IOException ioex) {
150 // Problems reading arguments from stream; could be wrong number of arguments
151 log.warn("Could not read arguments from stream, aborting request!", ioex);
152 return null;
153 }
154
155 // Try to invoke the server whose method is associated with the given hash
156 Object server = m.getServer();
157
158 // Do block invocation if we are pending a new view installtion.
159 // doBlock(m);
160
161 if (log.isDebugEnabled())
162 log.debug("Invoking " + m + " on server " + server.getClass().getSimpleName());
163 try {
164 return new InvocationResult(method, params, server, serverView, clientView);
165 } catch (IllegalAccessException e) {
166 log.warn("Tried to invoke the non-public method: " + m, e);
167 return null;
168 } catch (IllegalArgumentException e) {
169 log.warn("An IllegalArgumentException occured while invoking method: " + m, e);
170 return null;
171 }
172 }
173
174 /**
175 * Do block invocation if we are pending a new view.
176 *
177 * @param m the method which may get blocked
178 */
179 private void doBlock(MethodDetails m)
180 {
181 lock.lock();
182 try {
183 while (blockInvocation) {
184 if (log.isDebugEnabled())
185 log.debug("Blocking invocation " + m);
186 // wait for signal to start dispatching again
187 notDispatching.awaitUninterruptibly();
188 if (!blockInvocation && log.isDebugEnabled())
189 log.debug("Unblocking invocation " + m);
190 }
191 } finally {
192 lock.unlock();
193 }
194 }
195
196 /**
197 * Update local view, and unblock invocation dispatching.
198 */
199 void enableDispatching()
200 {
201 if (log.isDebugEnabled())
202 log.debug("Enable invocation dispatching");
203 lock.lock();
204 try {
205 // Unblock all pending invocations
206 blockInvocation = false;
207 notDispatching.signalAll();
208 } finally {
209 lock.unlock();
210 }
211 }
212
213 /**
214 * To block invocation dispatching during view change periods.
215 */
216 void disableDispatching()
217 {
218 if (log.isDebugEnabled())
219 log.debug("Disable invocation dispatching");
220 lock.lock();
221 try {
222 // Block new invocations from now on and until enableDispatching is called
223 blockInvocation = true;
224 } finally {
225 lock.unlock();
226 }
227 }
228
229 /**
230 * Set the server-side view for this invocation.
231 */
232 void setView(View serverView)
233 {
234 this.serverView = serverView;
235 }
236
237
238 /* (non-Javadoc)
239 * @see net.jini.jeri.InvocationDispatcher#dispatch(java.rmi.Remote, net.jini.jeri.InboundRequest, java.util.Collection)
240 */
241 public void dispatch(Remote impl, InboundRequest request, Collection context)
242 {
243 throw new UnsupportedOperationException("Should not be called.");
244 }
245
246 } // END GroupInvocationDispatcher