001: package org.jacorb.poa;
002:
003: /*
004: * JacORB - a free Java ORB
005: *
006: * Copyright (C) 1997-2004 Gerald Brose.
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Library General Public
010: * License as published by the Free Software Foundation; either
011: * version 2 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Library General Public License for more details.
017: *
018: * You should have received a copy of the GNU Library General Public
019: * License along with this library; if not, write to the Free
020: * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
021: */
022:
023: import java.util.*;
024:
025: import org.jacorb.poa.except.POAInternalError;
026: import org.jacorb.poa.util.ByteArrayKey;
027: import org.jacorb.poa.util.POAUtil;
028: import org.jacorb.poa.util.StringPair;
029:
030: import org.omg.CORBA.OBJ_ADAPTER;
031: import org.omg.PortableServer.POAPackage.ObjectAlreadyActive;
032: import org.omg.PortableServer.POAPackage.ObjectNotActive;
033: import org.omg.PortableServer.POAPackage.ServantAlreadyActive;
034: import org.omg.PortableServer.Servant;
035: import org.omg.PortableServer.ServantActivator;
036:
037: import org.apache.avalon.framework.logger.Logger;
038:
039: /**
040: * This class maps object id's to servants and vice versa.
041: * A oid/servant pair can be added/removed using add(),remove().
042: * The data can be retrieved using getServant() or getObjectId().
043: *
044: * @author Reimo Tiedemann, FU Berlin
045: * @version $Id: AOM.java,v 1.33 2006/07/27 13:50:35 nick.cross Exp $
046: */
047:
048: public class AOM {
049: private AOMListener aomListener;
050:
051: private final boolean unique;
052: private final Logger logger;
053:
054: // an ObjectID can appear only once, but an servant can have multiple ObjectId's
055: // if MULTIPLE_ID is set
056: private final Hashtable objectMap = new Hashtable(); // oid -> servant
057:
058: // only meaningful if UNIQUE_ID is set
059: // only for performance improvements (brose: is that still true?)
060: private Hashtable servantMap; // servant -> oid
061:
062: // for synchronisation of servant activator calls
063: private final Vector etherealisationList = new Vector();
064: private final Vector incarnationList = new Vector();
065:
066: private final Vector deactivationList = new Vector();
067: /**
068: * <code>deactivationListLock</code> is a lock to protect two consecutive
069: * operations on the list, used in remove().
070: */
071: private final byte[] deactivationListLock = new byte[0];
072:
073: protected AOM(boolean _unique, Logger _logger) {
074: unique = _unique;
075: logger = _logger;
076:
077: if (unique) {
078: servantMap = new Hashtable();
079: }
080: }
081:
082: /**
083: * <code>add</code> is called by the POA when activating an object
084: * to add a Servant into the Active Object Map.
085: *
086: * @param oid a <code>byte[]</code>, the id to use.
087: * @param servant a <code>Servant</code>, the servant to store.
088: * @exception ObjectAlreadyActive if an error occurs
089: * @exception ServantAlreadyActive if an error occurs
090: */
091: protected synchronized void add(byte[] oid, Servant servant)
092: throws ObjectAlreadyActive, ServantAlreadyActive {
093: ByteArrayKey oidbak = new ByteArrayKey(oid);
094:
095: /* an inCarnation and activation with the same oid has
096: priority, a reactivation for the same oid blocks until
097: etherealization is complete */
098:
099: while (incarnationList.contains(oidbak)
100: || etherealisationList.contains(oidbak) ||
101: // This is to check whether we are currently deactivating an
102: // object with the same id - if so, wait for the deactivate
103: // thread to complete.
104: deactivationList.contains(oidbak) ||
105: // This is to check whether we are attempting to reactivate
106: // a servant that is currently being deactivated with another ID.
107: (servantMap != null && servantMap.get(servant) != null && deactivationList
108: .contains(servantMap.get(servant)))) {
109: try {
110: wait();
111: } catch (InterruptedException e) {
112: }
113: }
114:
115: if (objectMap.containsKey(oidbak)) {
116: throw new ObjectAlreadyActive();
117: }
118:
119: if (unique && servantMap.containsKey(servant)) {
120: throw new ServantAlreadyActive();
121: }
122:
123: /* this is the actual object activation: */
124:
125: objectMap.put(oidbak, servant);
126:
127: if (unique) {
128: servantMap.put(servant, oidbak);
129: }
130:
131: if (logger.isInfoEnabled()) {
132: logger.info("oid: " + POAUtil.convert(oid)
133: + "object is activated");
134: }
135:
136: // notify an aom listener
137: if (aomListener != null) {
138: aomListener.objectActivated(oid, servant, objectMap.size());
139: }
140: }
141:
142: protected synchronized void addAOMListener(AOMListener listener) {
143: aomListener = EventMulticaster.add(aomListener, listener);
144: }
145:
146: boolean isDeactivating(ByteArrayKey oid) {
147: return deactivationList.contains(oid);
148: }
149:
150: protected boolean contains(Servant servant) {
151: if (unique) {
152: return servantMap.containsKey(servant);
153: } else {
154: return objectMap.contains(servant);
155: }
156: }
157:
158: protected synchronized StringPair[] deliverContent() {
159: StringPair[] result = new StringPair[objectMap.size()];
160: ByteArrayKey oidbak;
161: Enumeration en = objectMap.keys();
162:
163: for (int i = 0; i < result.length; i++) {
164: oidbak = (ByteArrayKey) en.nextElement();
165: result[i] = new StringPair(oidbak.toString(), objectMap
166: .get(oidbak).getClass().getName());
167: }
168: return result;
169:
170: }
171:
172: protected byte[] getObjectId(Servant servant) {
173: if (!unique) {
174: throw new POAInternalError(
175: "error: not UNIQUE_ID policy (getObjectId)");
176: }
177:
178: ByteArrayKey oidbak = (ByteArrayKey) servantMap.get(servant);
179:
180: if (oidbak != null) {
181: return oidbak.getBytes();
182: }
183:
184: return null;
185: }
186:
187: protected Servant getServant(byte[] oid) {
188: return (Servant) objectMap.get(new ByteArrayKey(oid));
189: }
190:
191: protected synchronized Servant incarnate(byte[] oid,
192: ServantActivator servant_activator,
193: org.omg.PortableServer.POA poa)
194: throws org.omg.PortableServer.ForwardRequest {
195: ByteArrayKey oidbak = new ByteArrayKey(oid);
196: Servant servant = null;
197:
198: if (logger.isInfoEnabled()) {
199: logger.info("oid: " + POAUtil.convert(oid) + "incarnate");
200: }
201:
202: /* all invocations of incarnate on the servant manager are serialized */
203: /* all invocations of etherealize on the servant manager are serialized */
204: /* invocations of incarnate and etherialize are mutually exclusive */
205:
206: while (!incarnationList.isEmpty()
207: || !etherealisationList.isEmpty()) {
208: try {
209: wait();
210: } catch (InterruptedException e) {
211: }
212: }
213:
214: /* another thread was faster, the incarnation is unnecessary now */
215: if (objectMap.containsKey(oidbak)) {
216: return (Servant) objectMap.get(oidbak);
217: }
218:
219: /* servant incarnation */
220: if (servant_activator == null) {
221: // This might be thrown if they failed to set implname
222: throw new OBJ_ADAPTER("Servant Activator for "
223: + POAUtil.convert(oid) + " was null.");
224: }
225:
226: incarnationList.addElement(oidbak);
227: try {
228: servant = servant_activator.incarnate(oid, poa);
229: } finally {
230: incarnationList.removeElement(oidbak);
231: notifyAll();
232: }
233:
234: if (servant == null) {
235: if (logger.isInfoEnabled()) {
236: logger
237: .info("oid: "
238: + POAUtil.convert(oid)
239: + "servant is not incarnated (incarnate returns null)");
240: }
241: } else {
242: if (unique && servantMap.containsKey(servant)) {
243: if (logger.isInfoEnabled()) {
244: logger
245: .info("oid: "
246: + POAUtil.convert(oid)
247: + "servant is not incarnated (unique_id policy is violated)");
248: }
249: servant = null;
250: } else {
251: if (logger.isDebugEnabled()) {
252: logger.debug("oid: " + POAUtil.convert(oid)
253: + "servant is incarnated");
254: }
255:
256: // notify an aom listener
257: if (aomListener != null) {
258: aomListener.servantIncarnated(oid, servant);
259: }
260:
261: /* object activation */
262:
263: try {
264: add(oid, servant);
265: } catch (ObjectAlreadyActive e) {
266: throw new POAInternalError(
267: "error: object already active (AOM.incarnate)");
268: } catch (ServantAlreadyActive e) {
269: throw new POAInternalError(
270: "error: servant already active (AOM.incarnate)");
271: }
272: }
273: }
274:
275: ((org.jacorb.poa.POA) poa).getORB().set_delegate(servant);
276:
277: return servant;
278: }
279:
280: protected void remove(byte[] oid,
281: RequestController requestController,
282: ServantActivator servantActivator, POA poa,
283: boolean cleanupInProgress) throws ObjectNotActive {
284: ByteArrayKey oidbak = new ByteArrayKey(oid);
285:
286: // check that the same oid is not already being deactivated
287: // (this must be synchronized to avoid having two independent
288: // threads register the same oid)
289: synchronized (deactivationListLock) {
290: if (!objectMap.containsKey(oidbak)
291: || deactivationList.contains(oidbak)) {
292: throw new ObjectNotActive();
293: }
294:
295: deactivationList.addElement(oidbak);
296: }
297:
298: final byte[] oid_ = oid;
299: final RequestController requestController_ = requestController;
300: final ServantActivator servantActivator_ = servantActivator;
301: final POA poa_ = poa;
302: final boolean cleanupInProgress_ = cleanupInProgress;
303:
304: Thread thread = new Thread("AOM_RemovalThread") {
305: public void run() {
306: _remove(oid_, requestController_, servantActivator_,
307: poa_, cleanupInProgress_);
308: }
309: };
310:
311: thread.start();
312: }
313:
314: /**
315: * <code>_remove</code> is spawned by remove to allow deactivate_object
316: * to return immediately.
317: *
318: * @param oid a <code>byte[]</code>, the id to use.
319: * @param requestController a <code>RequestController</code> value
320: * @param servantActivator a <code>ServantActivator</code> value
321: * @param poa a <code>POA</code> value
322: * @param cleanupInProgress a <code>boolean</code> value
323: */
324: private void _remove(byte[] oid,
325: RequestController requestController,
326: ServantActivator servantActivator, POA poa,
327: boolean cleanupInProgress) {
328: ByteArrayKey oidbak = new ByteArrayKey(oid);
329: Servant servant = null;
330:
331: if (!objectMap.containsKey(oidbak)) {
332: // should not happen but ...
333: deactivationList.removeElement(oidbak);
334: return;
335: }
336:
337: // wait for request completion on this object (see freeObject below)
338: if (requestController != null) {
339: requestController.waitForObjectCompletion(oid);
340: }
341:
342: synchronized (this ) {
343: if ((servant = (Servant) objectMap.get(oidbak)) == null) {
344: return;
345: }
346:
347: /* object deactivation */
348:
349: objectMap.remove(oidbak);
350:
351: if (unique) {
352: servantMap.remove(servant);
353: }
354:
355: // Wait to remove the oid from the deactivationList here so that the
356: // object map can be cleared out first. This ensures we don't
357: // reactivate an object we're currently deactivating.
358: deactivationList.removeElement(oidbak);
359:
360: if (logger.isInfoEnabled()) {
361: logger.info("oid: " + POAUtil.convert(oid)
362: + "object is deactivated");
363: }
364:
365: // notify an aom listener
366: if (aomListener != null) {
367: aomListener.objectDeactivated(oid, servant, objectMap
368: .size());
369: }
370:
371: if (servantActivator == null) {
372: requestController.freeObject(oid);
373: // Tell anyone waiting we're done now.
374: notifyAll();
375: return;
376: }
377:
378: /* servant etherealization */
379:
380: /* all invocations of incarnate on the servant manager are
381: serialized, all invocations of etherealize on the
382: servant manager are serialized, invocations of incarnate
383: and etherialize are mutually exclusive */
384:
385: while (!incarnationList.isEmpty()
386: || !etherealisationList.isEmpty()) {
387: try {
388: wait();
389: } catch (InterruptedException e) {
390: }
391: }
392: etherealisationList.addElement(oidbak);
393:
394: try {
395: servantActivator.etherealize(oid, poa, servant,
396: cleanupInProgress, contains(servant));
397:
398: if (logger.isInfoEnabled()) {
399: logger.info("oid: " + POAUtil.convert(oid)
400: + "servant is etherealized");
401: }
402:
403: // notify an aom listener
404:
405: if (aomListener != null) {
406: aomListener.servantEtherialized(oid, servant);
407: }
408: } catch (org.omg.CORBA.SystemException e) {
409: if (logger.isWarnEnabled()) {
410: logger
411: .info("oid: "
412: + POAUtil.convert(oid)
413: + "exception occurred during servant etherialisation: "
414: + e.getMessage());
415: }
416: } finally {
417: etherealisationList.removeElement(oidbak);
418: notifyAll();
419: }
420:
421: // unregister the object from deactivation list
422: if (requestController != null) {
423: requestController.freeObject(oid);
424: }
425: }
426: }
427:
428: protected void removeAll(ServantActivator servant_activator,
429: POA poa, boolean cleanup_in_progress) {
430: byte[] oid;
431: Enumeration en = objectMap.keys();
432: while (en.hasMoreElements()) {
433: oid = ((ByteArrayKey) en.nextElement()).getBytes();
434: _remove(oid, null, servant_activator, poa,
435: cleanup_in_progress);
436: }
437: }
438:
439: protected synchronized void removeAOMListener(AOMListener listener) {
440: aomListener = EventMulticaster.remove(aomListener, listener);
441: }
442:
443: protected int size() {
444: return objectMap.size();
445: }
446: }
|