View Javadoc

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