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">•
72 * <th scope="col" align="left" colspan="2"><code>
73 * activationIdPreparer</code>
74 * <tr valign="top"> <td> <th scope="row" align="right">
75 * Type: <td> {@link net.jini.security.ProxyPreparer}
76 * <tr valign="top"> <td> <th scope="row" align="right">
77 * Default: <td> <code>
78 * new {@link net.jini.security.BasicProxyPreparer}()
79 * </code>
80 * <tr valign="top"> <td> <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">•
94 * <th scope="col" align="left" colspan="2"><code>
95 * activationSystemPreparer</code>
96 * <tr valign="top"> <td> <th scope="row" align="right">
97 * Type: <td> {@link net.jini.security.ProxyPreparer}
98 * <tr valign="top"> <td> <th scope="row" align="right">
99 * Default: <td> <code>
100 * new {@link net.jini.security.BasicProxyPreparer}()</code>
101 * <tr valign="top"> <td> <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">•
116 * <th scope="col" align="left" colspan="2"><code>
117 * exporter</code>
118 * <tr valign="top"> <td> <th scope="row" align="right">
119 * Type: <td> {@link net.jini.export.Exporter}
120 * <tr valign="top"> <td> <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> <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">•
141 * <th scope="col" align="left" colspan="2"><code>
142 * loginContext</code>
143 * <tr valign="top"> <td> <th scope="row" align="right">
144 * Type: <td> {@link javax.security.auth.login.LoginContext}
145 * <tr valign="top"> <td> <th scope="row" align="right">
146 * Default: <td> <code>null</code>
147 * <tr valign="top"> <td> <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 }