View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership. The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License. You may obtain a copy of the License at
9    * 
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.river.start.group.impl;
20  
21  import org.apache.river.api.util.Startable;
22  import org.apache.river.start.group.SharedGroup;
23  import java.io.IOException;
24  import java.rmi.MarshalledObject;
25  import java.rmi.Remote;
26  import java.rmi.RemoteException;
27  import java.rmi.activation.ActivationException;
28  import java.rmi.activation.ActivationGroup;
29  import java.rmi.activation.ActivationID;
30  import java.rmi.activation.ActivationSystem;
31  import java.rmi.server.ExportException;
32  import java.security.AccessControlContext;
33  import java.security.AccessController;
34  import java.security.PrivilegedActionException;
35  import java.security.PrivilegedExceptionAction;
36  import java.util.logging.Level;
37  import java.util.logging.Logger;
38  import javax.security.auth.Subject;
39  import javax.security.auth.login.LoginContext;
40  import net.jini.config.Configuration;
41  import net.jini.config.ConfigurationException;
42  import net.jini.config.ConfigurationProvider;
43  import net.jini.core.constraint.RemoteMethodControl;
44  import net.jini.export.Exporter;
45  import net.jini.export.ProxyAccessor;
46  import net.jini.io.MarshalledInstance;
47  import net.jini.security.TrustVerifier;
48  import net.jini.security.proxytrust.ServerProxyTrust;
49  import org.apache.river.proxy.BasicProxyTrustVerifier;
50  
51  /**
52   * The provided implementation
53   * of the {@link SharedGroup} service.
54   *
55   * The following items are discussed below:
56   * <ul>
57   * <li><a href="#configEntries">Configuring SharedGroupImpl</a>
58   * <li><a href="#logging">Logging</a>
59   * </ul>
60   *
61   * <a name="configEntries"></a>
62   * <h3>Configuring SharedGroupImpl</h3>
63   *
64   * This implementation of <code>SharedGroupImpl</code> supports the
65   * following configuration entries, with component
66   * <code>org.apache.river.start</code>:
67   *
68   *   <table summary="Describes the activationIdPreparer configuration entry"
69   *          border="0" cellpadding="2">
70   *     <tr valign="top">
71   *       <th scope="col">&#X2022;
72   *       <th scope="col" align="left" colspan="2"><code>
73   *       activationIdPreparer</code>
74   *     <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
75   *       Type: <td> {@link net.jini.security.ProxyPreparer}
76   *     <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
77   *       Default: <td> <code>
78   *         new {@link net.jini.security.BasicProxyPreparer}()
79   *         </code>
80   *     <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
81   *       Description: <td> The proxy preparer for the service's activation
82   *       ID. The value should not be <code>null</code>. 
83   * 
84   *       The service starter calls the  
85   *       {@link java.rmi.activation.ActivationID#activate
86   *       activate} method to activate the service.
87   *   </table>
88   *
89   *   <table summary="Describes the activationSystemPreparer configuration
90   *          entry"
91   *          border="0" cellpadding="2">
92   *     <tr valign="top">
93   *       <th scope="col">&#X2022;
94   *       <th scope="col" align="left" colspan="2"><code>
95   *       activationSystemPreparer</code>
96   *     <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
97   *       Type: <td> {@link net.jini.security.ProxyPreparer}
98   *     <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
99   *       Default: <td> <code>
100  *         new {@link net.jini.security.BasicProxyPreparer}()</code>
101  *     <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
102  *       Description: <td> The proxy preparer for the proxy for the
103  *       activation system. The value should not be <code>null</code>. This
104  *       entry is obtained at service start and restart. 
105  * 
106  *       The service calls the {@link
107  *       java.rmi.activation.ActivationSystem#unregisterGroup
108  *       unregisterGroup} method on the {@link
109  *       java.rmi.activation.ActivationSystem} upon service destruction.
110  *   </table>
111  *
112  *   <table summary="Describes the exporter configuration entry"
113  *          border="0" cellpadding="2">
114  *     <tr valign="top">
115  *       <th scope="col">&#X2022;
116  *       <th scope="col" align="left" colspan="2"><code>
117  *       exporter</code>
118  *     <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
119  *       Type: <td> {@link net.jini.export.Exporter}
120  *     <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
121  *       Default: <td>
122  * <pre>
123  * new {@link net.jini.activation.ActivationExporter}(
124  *     <i>activationID</i>,
125  *     new {@link net.jini.jeri.BasicJeriExporter}(
126  *         {@link net.jini.jeri.tcp.TcpServerEndpoint#getInstance TcpServerEndpoint.getInstance}(0),
127  *         new {@link net.jini.jeri.BasicILFactory}(), false, true))
128  * </pre>
129  *     <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
130  *       Description: <td> The object to use for exporting the service. The
131  *       value should not be <code>null</code>. The call to 
132  *       <code>getEntry</code> will supply the activation ID in
133  *       the <code>data</code> argument. This entry is obtained at service
134  *       start and restart.
135  *   </table> <p>
136  *  
137  *   <table summary="Describes the loginContext configuration entry"
138  *     border="0" cellpadding="2">
139  *   <tr valign="top">
140  *     <th scope="col">&#X2022;
141  *     <th scope="col" align="left" colspan="2"><code>
142  *   loginContext</code>
143  *   <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
144  *     Type: <td> {@link javax.security.auth.login.LoginContext}
145  *   <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
146  *     Default: <td> <code>null</code>
147  *   <tr valign="top"> <td> &nbsp; <th scope="row" align="right">
148  *     Description: <td> If not <code>null</code>, specifies the JAAS
149  *     login context to use for performing a JAAS login and supplying the
150  *     {@link javax.security.auth.Subject} to use when running the
151  *     service. If <code>null</code>, no JAAS login is performed. This
152  *     entry is obtained at service start and restart.
153  *   </table>
154  *
155  *
156  *<a name="logging"></a>
157  *<h3>Loggers and Logging Levels</h3>
158  *
159  *The SharedGroupImpl service implementation uses the {@link
160  *java.util.logging.Logger}, named <code>org.apache.river.sharedGroup</code>. 
161  *The following table describes the
162  *type of information logged as well as the levels of information logged.
163  *<p>
164  *
165  *  <table border="1" cellpadding="5"
166  *	 summary="Describes logging performed by sharedGroup at different
167  *	 logging levels">
168  *
169  *  <caption><b><code>
170  *	   org.apache.river.start.group.SharedGroup</code></b></caption>
171  *
172  *  <tr> <th scope="col"> Level <th scope="col"> Description
173  *
174  *  <tr> <td> {@link java.util.logging.Level#FINE FINE} <td> 
175  *    for low level
176  *    service operation tracing
177  *  <tr> <td> {@link java.util.logging.Level#FINER FINER} <td> 
178  *    for lower level
179  *    service operation tracing
180  *  <tr> <td> {@link java.util.logging.Level#FINEST FINEST} <td> 
181  *    for lowest level
182  *    service operation tracing
183  *
184  *  </table> <p>
185  * 
186  * @author Sun Microsystems, Inc.
187  *
188  */
189 public class SharedGroupImpl implements Remote, 
190        SharedGroup,
191        ServerProxyTrust, 
192        ProxyAccessor, Startable {
193     
194     /** Component name for configuration entries */
195     static final String START_PACKAGE = "org.apache.river.start.group";
196 
197     /** Configure logger */
198     static final Logger logger =
199         Logger.getLogger(START_PACKAGE + ".SharedGroup");
200 
201     /** Our prepared activation ID reference */
202     private final ActivationID activationID;
203 
204     /** The prepared activation system reference */
205     private volatile ActivationSystem activationSystem;
206     
207     /** The inner proxy of this server */
208     private Remote ourStub;
209 
210     /** <code>LoginContext</code> for this service. */
211     private final LoginContext loginContext;
212     
213     /** The exporter for exporting and unexporting */
214     protected final Exporter exporter;
215     
216     private final AccessControlContext context;
217 
218     /**
219      * Activation constructor. 
220      */
221     SharedGroupImpl(ActivationID activationID, MarshalledObject data)
222 	throws Exception
223     {
224 	this(getInit(activationID, data));
225     }
226     
227     private static SharedGroupImplInit getInit(ActivationID activationID, 
228                                                             MarshalledObject data)
229             throws IOException, ClassNotFoundException, ConfigurationException, Exception
230     {
231         LoginContext loginContext = null;
232         try {
233             logger.entering(SharedGroupImpl.class.getName(), "SharedGroupImpl", 
234                 new Object[] { activationID, data}); 
235             String[] configArgs = (String[]) new MarshalledInstance(data).get(false);	
236             Configuration config = ConfigurationProvider.getInstance(configArgs);
237             loginContext = (LoginContext) config.getEntry(
238                 START_PACKAGE, "loginContext", LoginContext.class, null);
239             SharedGroupImplInit init = null;
240                 if (loginContext != null) {
241                     init = doInitWithLogin(config, activationID, loginContext);
242                 } else {
243                     init = doInit(config, activationID, null);
244                 }
245             return init;
246         } catch (Exception e) {
247             // If we get here an instance of SharedGroupImpl hasn't been
248             // created, it's constructor won't be called and exporting doesn't
249             // occur.
250             if (loginContext != null) {
251                 try {
252                     loginContext.logout();
253                     logger.finest("SharedGroupImpl logged-out.");
254                 } catch (Exception ex) {
255                     logger.log(Level.FINEST, 
256                         "Problem logging out for SharedGroupImpl.", ex);
257                 }
258             }
259             throw e;
260         } finally {
261             logger.exiting(SharedGroupImpl.class.getName(), "SharedGroupImpl");
262         }
263     }
264     
265     private SharedGroupImpl(SharedGroupImplInit init){
266             activationSystem = init.activationSystem;
267             this.activationID = init.activationID;
268             exporter = init.exporter;
269             context = init.context;
270             loginContext = init.loginContext;
271     }       
272 
273     private static SharedGroupImplInit doInitWithLogin(final Configuration config, 
274                                                     final ActivationID id, 
275                                                     final LoginContext loginContext) 
276                                                 throws Exception
277     {
278         loginContext.login();
279         try {
280             return Subject.doAsPrivileged(
281                 loginContext.getSubject(),
282                 new PrivilegedExceptionAction<SharedGroupImplInit>() {
283                     public SharedGroupImplInit run() throws Exception {
284                         return doInit(config, id, loginContext);
285                     }
286                 },
287                 null);
288         } catch (PrivilegedActionException e) {
289             throw e.getException();
290         }
291     }
292     
293     private static SharedGroupImplInit doInit(Configuration config, 
294                                                     ActivationID id, 
295                                                     LoginContext loginContext) 
296                                                             throws Exception 
297     {
298         return new SharedGroupImplInit(config, id, loginContext);
299     }
300     
301     public final synchronized void start() throws ExportException {
302         try {
303             // Export service
304             
305             ourStub = AccessController.doPrivileged(new PrivilegedExceptionAction<Remote>(){
306 
307                 @Override
308                 public Remote run() throws ExportException {
309                     return exporter.export(SharedGroupImpl.this);	
310                 }
311                 
312             }, context);
313         } catch (PrivilegedActionException ex) {
314             ExportException e = (ExportException) ex.getException();
315             cleanup();
316             throw e;
317         }
318                 	
319         logger.log(Level.FINEST, "Exported service proxy: {0}",
320             ourStub);
321     }
322 
323     // javadoc inherited from supertype
324     @Override
325     public void destroyVM() throws RemoteException, ActivationException {
326 	logger.entering(SharedGroupImpl.class.getName(), "destroyVM"); 
327 	/*
328 	 * Would like to synch access to activationSystem, but need
329 	 * to avoid holding locks across remote invocations.
330 	 */
331         if (activationSystem != null) {
332 	    activationSystem.unregisterGroup(
333 		ActivationGroup.currentGroupID());
334                 logger.finest("ActivationGroup unregistered.");
335 		/* Unregistering the group implicitly unregisters
336 		 * all the objects associated with that group as well.
337 		 */
338 	    activationSystem = null;     
339 	}
340         (new SharedGroupImpl.DestroyThread()).start();
341 	logger.exiting(SharedGroupImpl.class.getName(), "destroyVM"); 
342     }
343 
344     /**
345      * Private utility method which attempts to roll back from 
346      * from a failed initialization attempt. 
347      */
348     private void cleanup() {
349 	logger.entering(SharedGroupImpl.class.getName(), "cleanup"); 
350         /* 
351 	 * Caller decides whether or not to unregister this object.
352 	 */
353         if (exporter != null) {
354             try {
355 	        // Unexport object so that no new calls come in
356 	        exporter.unexport(true);
357                 logger.finest("SharedGroupImpl unexported.");
358 	    } catch (Exception e) {
359                 logger.log(Level.FINEST, 
360 		    "Problem unexporting SharedGroupImpl.", e);
361 	    }
362         }
363 		
364 	if (loginContext != null) {
365 	    try {
366 		loginContext.logout();
367                 logger.finest("SharedGroupImpl logged-out.");
368 	    } catch (Exception e) {
369                 logger.log(Level.FINEST, 
370 		    "Problem logging out for SharedGroupImpl.", e);
371 	    }
372 	}
373 	logger.exiting(SharedGroupImpl.class.getName(), "cleanup"); 
374     }
375 
376     /**
377      * Termination thread code.  We do this in a separate thread to
378      * allow the calling thread to return normally. This is not guaranteed
379      * since it's still possible for the VM to exit before the calling
380      * thread returns.
381      */
382     private class DestroyThread extends Thread {
383 
384         /** Create a non-daemon thread */
385         public DestroyThread() {
386             super("DestroyThread");
387             /* override inheritance from RMI daemon thread */
388             setDaemon(false);
389         }
390 
391         public void run() {
392             logger.entering(SharedGroupImpl.DestroyThread.class.getName(), 
393                 "run");
394  
395             logger.finest("Calling System.exit() ...");
396 
397 	    /*
398 	     * Forcefully destroy the VM, in case there are any lingering 
399 	     * threads.
400 	     */
401 	    System.exit(0);
402         }
403     }
404     // inherit javadoc
405     public synchronized Object getProxy() {
406         logger.entering(SharedGroupImpl.class.getName(), 
407                 "getProxy");
408         logger.exiting(SharedGroupImpl.class.getName(), 
409                 "getProxy", ourStub);
410 	return ourStub;
411     }
412     
413     //////////////////////////////////////////
414     // ProxyTrust Method
415     //////////////////////////////////////////
416     //inherit javadoc
417     public synchronized TrustVerifier getProxyVerifier( ) {
418         /* No verifier if the server isn't secure */
419         if (!(ourStub instanceof RemoteMethodControl)) {
420             throw new UnsupportedOperationException();
421         } else {
422             return new BasicProxyTrustVerifier(ourStub);
423         }
424     }
425 }