001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: *
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: /**
020: * @author Mikhail A. Markov
021: * @version $Revision: 1.1.2.3 $
022: */package org.apache.harmony.rmi.server;
023:
024: import java.lang.ref.ReferenceQueue;
025: import java.rmi.NoSuchObjectException;
026: import java.rmi.Remote;
027: import java.rmi.RemoteException;
028: import java.rmi.dgc.DGC;
029: import java.rmi.server.ExportException;
030: import java.rmi.server.ObjID;
031: import java.security.AccessController;
032:
033: import org.apache.harmony.rmi.common.CreateThreadAction;
034: import org.apache.harmony.rmi.common.InterruptThreadAction;
035: import org.apache.harmony.rmi.internal.nls.Messages;
036: import org.apache.harmony.rmi.remoteref.UnicastServerRef;
037:
038: /**
039: * Manager controlling all exported objects.
040: * It is put to org.apache.harmony.rmi.transport package because some methods
041: * should be made package protected.
042: *
043: * @author Mikhail A. Markov
044: * @version $Revision: 1.1.2.3 $
045: */
046: public class ExportManager {
047:
048: // List of all remote objects exported in this VM.
049: private static RMIObjectTable exportedObjs = new RMIObjectTable();
050:
051: // The only one instance of DGC per VM.
052: private static DGC dgcImpl = new DGCImpl();
053:
054: // Creates and exports DGC implementation.
055: static {
056: try {
057: UnicastServerRef dgcRef = new UnicastServerRef(0, null,
058: null, ClientDGC.DGC_ID);
059: dgcRef.exportObject(dgcImpl, null, false, false, true);
060: RMIObjectInfo dgcInfo = new RMIObjectInfo(new RMIReference(
061: dgcImpl), ClientDGC.DGC_ID, dgcRef, null);
062: exportedObjs.add(dgcInfo);
063: } catch (Exception ex) {
064: // rmi.7D=Unable to initialize DGC.
065: throw new Error(Messages.getString("rmi.7D"), ex); //$NON-NLS-1$
066: }
067: }
068:
069: // Queue to wait for objects to be collected.
070: private static ReferenceQueue dgcQueue = new ReferenceQueue();
071:
072: // Thread removing GC-collected objects from the table of Exported objects.
073: private static Thread scav;
074:
075: // Number of in-progress calls to the objects in this table.
076: private static int activeCallsNum = 0;
077:
078: // Number of non system (requiring VM-blocking thread) objects.
079: private static int nonSystemObjsNum = 0;
080:
081: // lock object for working with active calls
082: private static class CallsLock {
083: }
084:
085: private static Object callsLock = new CallsLock();
086:
087: /**
088: * Exports specified remote object through pre-initialized UnicastServerRef.
089: * Returns info for exported object. If object has already been exported,
090: * ExportException will be thrown. The thread listening for incoming
091: * connections will be started.
092: *
093: * @param obj remote object to be exported
094: * @param sref initialized UnicastServerRef to export object through
095: * @param useProxyStubs If true then Proxy stubs will be generated if stub
096: * class could not be found in classpath and codebase; if false Proxy
097: * stubs will not be tried (this is needed for
098: * UnicastRemoteObject.exportObject(Remote) method because it
099: * returns RemoteStub class (but Proxy class could not be casted
100: * to it)
101: *
102: * @return stub for exported object
103: *
104: * @throws RemoteException if any exception occurred while exporting
105: * specified remote object
106: */
107: public static Remote exportObject(Remote obj,
108: UnicastServerRef sref, boolean useProxyStubs)
109: throws RemoteException {
110: return exportObject(obj, sref, useProxyStubs, true, false);
111: }
112:
113: /**
114: * Exports specified remote object through pre-initialized UnicastServerRef.
115: * Returns info for exported object. If object has already been exported,
116: * ExportException will be thrown.
117: *
118: * @param obj remote object to be exported
119: * @param sref initialized UnicastServerRef to export object through
120: * @param useProxyStubs If true then Proxy stubs will be generated if stub
121: * class could not be found in classpath and codebase; if false Proxy
122: * stubs will not be tried (this is needed for
123: * UnicastRemoteObject.exportObject(Remote) method because it
124: * returns RemoteStub class (but Proxy class could not be casted
125: * to it)
126: * @param startListen if false, ServerSocket listening thread will not be
127: * started (this is used for DGC, for example); otherwise listening
128: * thread will be started and object becomes available for
129: * connections from clients
130: * @param isSystem if true then existence of this object will not prevent
131: * VM from exiting (for example, for rmiregistry)
132: *
133: * @return stub for exported object
134: *
135: * @throws RemoteException if any exception occurred while exporting
136: * specified remote object
137: */
138: public static Remote exportObject(Remote obj,
139: UnicastServerRef sref, boolean useProxyStubs,
140: boolean startListen, boolean isSystem)
141: throws RemoteException {
142: if (isExported(obj)) {
143: // rmi.7B=Object {0} has already been exported.
144: throw new ExportException(Messages.getString("rmi.7B", obj)); //$NON-NLS-1$
145: }
146: Remote stub = sref.exportObject(obj, null, useProxyStubs,
147: startListen, isSystem);
148: RMIReference rref = new RMIReference(obj, dgcQueue);
149: RMIObjectInfo info = new RMIObjectInfo(rref, sref.getObjId(),
150: sref, stub);
151: exportedObjs.add(info);
152:
153: if (scav == null) {
154: (scav = (Thread) AccessController
155: .doPrivileged(new CreateThreadAction(
156: new Scavenger(), "Scavenger", false))).start(); //$NON-NLS-1$
157: }
158:
159: if (isSystem) {
160: rref.makeStrong(true);
161: } else {
162: ++nonSystemObjsNum;
163: }
164: return stub;
165: }
166:
167: /**
168: * Unexports specified remote object so it becomes unavailable for
169: * receiving remote calls. If force parameter is false then the object will
170: * be unexported only if there are no pending or in-progress remote calls
171: * to it, otherwise (if force parameter is true) the object will be
172: * unexported forcibly.
173: *
174: * @param obj remote object to be unexported
175: * @param force if false then specified object will only be unexported if
176: * there are no pending or in-progress calls to it; otherwise
177: * the object will be unexported forcibly (even if there are such
178: * calls)
179: *
180: * @throws NoSuchObjectException if specified object has not been exported
181: * or has already been unexported
182: */
183: public static boolean unexportObject(Remote obj, boolean force)
184: throws NoSuchObjectException {
185: RMIReference ref = new RMIReference(obj);
186: RMIObjectInfo info = exportedObjs.getByRef(ref);
187:
188: if (info == null) {
189: // rmi.7C=Object {0} is not exported.
190: throw new NoSuchObjectException(Messages.getString(
191: "rmi.7C", obj)); //$NON-NLS-1$
192: }
193: boolean succeeded = info.sref.unexportObject(force);
194:
195: if (succeeded) {
196: exportedObjs.removeByRef(ref);
197:
198: synchronized (callsLock) {
199: if (!info.sref.isSystem()) {
200: --nonSystemObjsNum;
201: }
202: scavInterrupt();
203: }
204: }
205: return succeeded;
206: }
207:
208: /**
209: * Returns stub for specified remote object, or throws NoSuchObjectException
210: * if object was not exported via this class.
211: *
212: * @param obj remote object for which stub is needed
213: *
214: * @return stub for specified remote object if it was exported
215: *
216: * @throws NoSuchObjectException if specified object was not exported via
217: * this class
218: */
219: public static Remote getStub(Remote obj)
220: throws NoSuchObjectException {
221: RMIObjectInfo info = exportedObjs
222: .getByRef(new RMIReference(obj));
223:
224: if (info == null) {
225: // rmi.7C=Object {0} is not exported.
226: throw new NoSuchObjectException(Messages.getString(
227: "rmi.7C", obj)); //$NON-NLS-1$
228: }
229: return info.stub;
230: }
231:
232: /**
233: * Returns true if specified remote object was exported via this class.
234: *
235: * @param obj remote object to check
236: *
237: * @return true if specified remote object was exported via this class
238: */
239: public static boolean isExported(Remote obj) {
240: return exportedObjs.containsByRef(new RMIReference(obj));
241: }
242:
243: /*
244: * Returns RMIObjectInfo in the list of exported objects using the given
245: * Object ID as a key.
246: *
247: * @param id Object ID to be used as a key
248: *
249: * @return RMIObjectInfo found
250: */
251: static RMIObjectInfo getInfo(ObjID id) {
252: return exportedObjs.getById(id);
253: }
254:
255: /*
256: * Increase the number of active calls by one.
257: */
258: static void addActiveCall() {
259: synchronized (callsLock) {
260: ++activeCallsNum;
261:
262: if (scav == null) {
263: (scav = (Thread) AccessController
264: .doPrivileged(new CreateThreadAction(
265: new Scavenger(), "Scavenger", false))).start(); //$NON-NLS-1$
266: }
267: }
268: }
269:
270: /*
271: * Decrease the number of active calls by one.
272: */
273: static void removeActiveCall() {
274: synchronized (callsLock) {
275: --activeCallsNum;
276: scavInterrupt();
277: }
278: }
279:
280: /*
281: * Interrupts thread removing objects from the table, if the number of
282: * active calls and number of exported system objects are both zero.
283: */
284: private static void scavInterrupt() {
285: if (activeCallsNum == 0 && nonSystemObjsNum == 0
286: && scav != null) {
287: AccessController.doPrivileged(new InterruptThreadAction(
288: scav));
289: scav = null;
290: }
291: }
292:
293: /*
294: * Thread removing objects from the table when they are scheduled for GC.
295: * It blocks VM from exiting as it's run with setDaemon(false).
296: */
297: private static class Scavenger implements Runnable {
298: public void run() {
299: try {
300: do {
301: // this operation blocks the thread
302: RMIReference ref = (RMIReference) dgcQueue.remove();
303:
304: // removes objects from the table
305: RMIObjectInfo info = exportedObjs.removeByRef(ref);
306:
307: synchronized (callsLock) {
308: if (info != null) {
309: if (!info.sref.isSystem()) {
310: --nonSystemObjsNum;
311: }
312: }
313:
314: if (nonSystemObjsNum == 0
315: && activeCallsNum == 0) {
316: break;
317: }
318: }
319: } while (!Thread.interrupted());
320: } catch (InterruptedException ie) {
321: }
322: scav = null;
323: }
324: }
325: }
|