001: ///////////////////////////////////////////////////////////////////////////////
002: //
003: // Copyright (C) 2003-@year@ by Thomas M. Hazel, MyOODB (www.myoodb.org)
004: //
005: // All Rights Reserved
006: //
007: // This program is free software; you can redistribute it and/or modify
008: // it under the terms of the GNU General Public License and GNU Library
009: // General Public License as published by the Free Software Foundation;
010: // either version 2, or (at your option) any later version.
011: //
012: // This program is distributed in the hope that it will be useful,
013: // but WITHOUT ANY WARRANTY; without even the implied warranty of
014: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: // GNU General Public License and GNU Library General Public License
016: // for more details.
017: //
018: // You should have received a copy of the GNU General Public License
019: // and GNU Library General Public License along with this program; if
020: // not, write to the Free Software Foundation, 675 Mass Ave, Cambridge,
021: // MA 02139, USA.
022: //
023: ///////////////////////////////////////////////////////////////////////////////
024: package org.myoodb.core;
025:
026: import java.io.*;
027: import java.util.*;
028: import java.lang.reflect.*;
029:
030: import org.myoodb.*;
031: import org.myoodb.exception.*;
032:
033: public abstract class AbstractObjectContainer implements Externalizable {
034: public final static int STATE_UNKNOWN = 0;
035: public final static int STATE_CREATED = 1;
036: public final static int STATE_ACTIVE = 2;
037: public final static int STATE_DELETED = 3;
038:
039: public static String IMPL_NAME_SUFFIX = "@implExtension@";
040: public static String BEAN_NAME_SUFFIX = "@beanExtension@";
041: public static String PROXY_NAME_SUFFIX = "@proxyExtension@";
042:
043: private volatile int m_state;
044: private volatile Identifier m_objectId;
045: private MyOodbLocal[] m_targets;
046:
047: protected AbstractCluster m_cluster;
048:
049: private transient MyOodbProxy m_myProxy;
050: private transient Constructor m_beanConstructor;
051: private volatile transient boolean m_dirtyFlag;
052:
053: public AbstractObjectContainer() {
054: }
055:
056: public AbstractObjectContainer(int state, Identifier objectId) {
057: m_state = state;
058: m_objectId = objectId;
059: }
060:
061: public int getState() {
062: return m_state;
063: }
064:
065: public void raiseState(int newState) {
066: m_state = newState > m_state ? newState : m_state;
067: }
068:
069: public AbstractLock getLock() {
070: return (m_cluster != null) ? m_cluster.getLock() : null;
071: }
072:
073: public void setCluster(AbstractCluster cluster) {
074: m_cluster = cluster;
075: }
076:
077: public AbstractCluster getCluster() {
078: return m_cluster;
079: }
080:
081: public boolean isDeleted() {
082: return (getState() == STATE_DELETED);
083: }
084:
085: public boolean isCreated() {
086: return (getState() == STATE_CREATED);
087: }
088:
089: public void setObjectIdentifier(Identifier objectId) {
090: m_objectId = objectId;
091: }
092:
093: public Identifier getObjectIdentifier() {
094: return m_objectId;
095: }
096:
097: public void setDirtyFlag(boolean flag) {
098: m_dirtyFlag = flag;
099: }
100:
101: public boolean getDirtyFlag() {
102: return m_dirtyFlag;
103: }
104:
105: public MyOodbLocal getTarget() {
106: return m_targets[0];
107: }
108:
109: public MyOodbLocal getTarget(int index) {
110: return m_targets[index];
111: }
112:
113: public MyOodbLocal[] getTargets() {
114: return m_targets;
115: }
116:
117: public void createTarget(Class[] classTypes, String sig,
118: Object[] args) throws Exception {
119: m_targets = new MyOodbLocal[classTypes.length];
120:
121: if (sig != null) {
122: Constructor constructor = MethodHelper.getConstructor(
123: classTypes[0], sig);
124: m_targets[0] = (MyOodbLocal) constructor.newInstance(args);
125: } else {
126: m_targets[0] = (MyOodbLocal) classTypes[0].newInstance();
127: }
128:
129: m_targets[0].setContainer(this );
130:
131: for (int i = 1; i < m_targets.length; i++) {
132: // TODO: handle more than just primary signatures
133: try {
134: Constructor constructor = MethodHelper.getConstructor(
135: classTypes[i], sig);
136: m_targets[i] = (MyOodbLocal) constructor
137: .newInstance(args);
138: } catch (NoSuchMethodException e) {
139: m_targets[i] = (MyOodbLocal) classTypes[i]
140: .newInstance();
141: }
142:
143: m_targets[i].setContainer(this );
144: }
145:
146: if (MyOodbManager.getTheManager().s_postObjectMethodCallback != null) {
147: MyOodbManager.getTheManager().s_postObjectMethodCallback
148: .invoke(null, new Object[] { m_targets[0],
149: "CONSTRUCTOR", new Object[] {} });
150: }
151:
152: for (int i = 0; i < m_targets.length; i++) {
153: m_targets[i].onCreate();
154: }
155:
156: raiseState(STATE_CREATED);
157: }
158:
159: public Object invokeTarget(String methodName, String sig,
160: Object[] args, int lockLevel) throws Exception {
161: m_cluster.setModificationTime(System.currentTimeMillis());
162:
163: Method primaryMethod = MethodHelper.getMethod(m_targets,
164: m_targets[0], methodName, sig, args);
165:
166: if (primaryMethod == null) {
167: throw new NoSuchMethodException("Target( " + m_objectId
168: + " ) Method Name: " + methodName);
169: }
170:
171: if (lockLevel > org.myoodb.MyOodbAccess.READ) {
172: if (MyOodbManager.getTheManager().s_preObjectMethodCallback != null) {
173: MyOodbManager.getTheManager().s_preObjectMethodCallback
174: .invoke(null, new Object[] { m_targets[0],
175: primaryMethod.getName(), args });
176: }
177: }
178:
179: Object result = primaryMethod.invoke(m_targets[0], args);
180:
181: if ((lockLevel > org.myoodb.MyOodbAccess.READ)
182: && (MyOodbManager.getTheManager().s_postObjectMethodCallback != null)) {
183: MyOodbManager.getTheManager().s_postObjectMethodCallback
184: .invoke(null, new Object[] { m_targets[0],
185: primaryMethod.getName(), args });
186: }
187:
188: if ((m_targets.length != 1)
189: && (lockLevel > org.myoodb.MyOodbAccess.READ)) {
190: try {
191: if (m_targets[0].getClass().isAssignableFrom(
192: DeclaredMethodDelegation.class) == true) {
193: m_targets[0].getClass().getDeclaredMethod(
194: primaryMethod.getName(),
195: primaryMethod.getParameterTypes());
196: } else {
197: m_targets[0].getClass().getMethod(
198: primaryMethod.getName(),
199: primaryMethod.getParameterTypes());
200: }
201: } catch (java.lang.NoSuchMethodException e1) {
202: for (int i = 1; i < m_targets.length; i++) {
203: Method secondaryMethod = MethodHelper.getMethod(
204: m_targets, m_targets[i], methodName, sig,
205: args);
206:
207: if (secondaryMethod == null) {
208: continue;
209: }
210:
211: try {
212: secondaryMethod.invoke(m_targets[i], args);
213: } catch (java.lang.IllegalArgumentException e2) {
214: // nothing to do
215: }
216: }
217: }
218: }
219:
220: return result;
221: }
222:
223: public Object invokeTarget(int methodIndex, Object[] args,
224: int lockLevel) throws Exception {
225: m_cluster.setModificationTime(System.currentTimeMillis());
226:
227: Class classType = m_targets[0].getClass();
228:
229: Method[] primaryMethods = MethodHelper.getMethods(classType);
230: Method primaryMethod = primaryMethods[methodIndex];
231:
232: if (primaryMethod == null) {
233: throw new NoSuchMethodException("Target( " + m_objectId
234: + " ) Method index: " + methodIndex);
235: }
236:
237: if (lockLevel > org.myoodb.MyOodbAccess.READ) {
238: if (MyOodbManager.getTheManager().s_preObjectMethodCallback != null) {
239: MyOodbManager.getTheManager().s_preObjectMethodCallback
240: .invoke(null, new Object[] { m_targets[0],
241: primaryMethod.getName(), args });
242: }
243: }
244:
245: Object result = primaryMethod.invoke(m_targets[0], args);
246:
247: if ((lockLevel > org.myoodb.MyOodbAccess.READ)
248: && (MyOodbManager.getTheManager().s_postObjectMethodCallback != null)) {
249: MyOodbManager.getTheManager().s_postObjectMethodCallback
250: .invoke(null, new Object[] { m_targets[0],
251: primaryMethod.getName(), args });
252: }
253:
254: if ((m_targets.length != 1)
255: && (lockLevel > org.myoodb.MyOodbAccess.READ)) {
256: try {
257: if (m_targets[0].getClass().isAssignableFrom(
258: DeclaredMethodDelegation.class) == true) {
259: m_targets[0].getClass().getDeclaredMethod(
260: primaryMethod.getName(),
261: primaryMethod.getParameterTypes());
262: } else {
263: m_targets[0].getClass().getMethod(
264: primaryMethod.getName(),
265: primaryMethod.getParameterTypes());
266: }
267: } catch (java.lang.NoSuchMethodException e1) {
268: for (int i = 1; i < m_targets.length; i++) {
269: classType = m_targets[i].getClass();
270:
271: try {
272: Method secondaryMethod = classType.getMethod(
273: primaryMethod.getName(), primaryMethod
274: .getParameterTypes());
275:
276: secondaryMethod.invoke(m_targets[i], args);
277: } catch (java.lang.NoSuchMethodException e2) {
278: // nothing to do
279: } catch (java.lang.IllegalArgumentException e3) {
280: // nothing to do
281: }
282: }
283: }
284: }
285:
286: return result;
287: }
288:
289: public void deleteTarget() throws Exception {
290: if (MyOodbManager.getTheManager().s_preObjectMethodCallback != null) {
291: MyOodbManager.getTheManager().s_preObjectMethodCallback
292: .invoke(null, new Object[] { m_targets[0],
293: "DESTRUCTOR", new Object[] {} });
294: }
295:
296: for (int i = 0; i < m_targets.length; i++) {
297: m_targets[i].onDelete();
298: }
299:
300: raiseState(STATE_DELETED);
301: }
302:
303: public void setBean(MyOodbBean bean) throws Exception {
304: m_cluster.setModificationTime(System.currentTimeMillis());
305:
306: if (getBean().getClass().equals(bean.getClass()) == false) {
307: throw new PermissionException("Target( " + m_objectId
308: + " ) Invalid set (bean mismatch): "
309: + bean.getClass());
310: }
311:
312: // TODO: cache methods for peformance
313: synchronized (this ) {
314: Method[] methods = MethodHelper.getMethods(m_targets[0]
315: .getClass());
316: for (int methodIndex = 0; methodIndex < methods.length; methodIndex++) {
317: Method setMethod = methods[methodIndex];
318: Method getMethod = MethodHelper
319: .getAssociatedBeanGetMethod(setMethod, methods);
320:
321: if (getMethod != null) {
322: try {
323: getMethod = bean.getClass().getDeclaredMethod(
324: getMethod.getName(),
325: getMethod.getParameterTypes());
326: Object retval = getMethod.invoke(bean,
327: new Object[] {});
328: setMethod.invoke(m_targets[0],
329: new Object[] { retval });
330: } catch (java.lang.NoSuchMethodException e) {
331: // not all sets are beanified
332: }
333:
334: continue;
335: }
336:
337: Method addMethod = methods[methodIndex];
338: Method getsMethod = MethodHelper
339: .getAssociatedBeanGetsMethod(addMethod, methods);
340: Method removeMethod = MethodHelper
341: .getAssociatedBeanRemoveMethod(addMethod,
342: methods);
343:
344: if ((getsMethod != null) && (removeMethod != null)) {
345: try {
346: ArrayList list = (ArrayList) getsMethod.invoke(
347: m_targets[0], new Object[] {});
348: for (int i = 0; i < list.size(); i++) {
349: removeMethod.invoke(m_targets[0],
350: new Object[] { list.get(i) });
351: }
352: getsMethod = bean.getClass().getDeclaredMethod(
353: getsMethod.getName(),
354: getsMethod.getParameterTypes());
355: list = (ArrayList) getsMethod.invoke(bean,
356: new Object[] {});
357: for (int i = 0; i < list.size(); i++) {
358: addMethod.invoke(m_targets[0],
359: new Object[] { list.get(i) });
360: }
361: } catch (java.lang.NoSuchMethodException e) {
362: // not all sets are beanified
363: }
364: }
365: }
366: }
367: }
368:
369: public MyOodbBean getBean() {
370: m_cluster.setModificationTime(System.currentTimeMillis());
371:
372: MyOodbBean myBean = null;
373:
374: try {
375: if (m_beanConstructor == null) {
376: synchronized (MethodHelper.CLASS_TABLE) {
377: if (m_beanConstructor == null) {
378: String beanName = m_targets[0].getClass()
379: .getName();
380: beanName = beanName.substring(0, beanName
381: .length()
382: - IMPL_NAME_SUFFIX.length())
383: + BEAN_NAME_SUFFIX;
384:
385: MethodSignature def = new MethodSignature(
386: beanName, "CONSTRUCTOR",
387: "org.myoodb.core.Identifier");
388: m_beanConstructor = (Constructor) MethodHelper.METHOD_TABLE
389: .get(def);
390:
391: if (m_beanConstructor == null) {
392: synchronized (MethodHelper.METHOD_TABLE) {
393: m_beanConstructor = (Constructor) MethodHelper.METHOD_TABLE
394: .get(def);
395: if (m_beanConstructor == null) {
396: Class classType = MyOodbManager
397: .getTheManager()
398: .getClassManager()
399: .getClass(beanName);
400: m_beanConstructor = classType
401: .getConstructor(new Class[] { Identifier.class });
402: MethodHelper.METHOD_TABLE.put(def,
403: m_beanConstructor);
404: }
405: }
406: }
407: }
408: }
409: }
410:
411: // TODO: cache methods for peformance
412: synchronized (this ) {
413: myBean = (MyOodbBean) m_beanConstructor
414: .newInstance(new Object[] { getObjectIdentifier() });
415:
416: Method[] methods = MethodHelper.getMethods(m_targets[0]
417: .getClass());
418: for (int methodIndex = 0; methodIndex < methods.length; methodIndex++) {
419: Method setMethod = methods[methodIndex];
420: Method getMethod = MethodHelper
421: .getAssociatedBeanGetMethod(setMethod,
422: methods);
423:
424: if (getMethod != null) {
425: try {
426: setMethod = myBean
427: .getClass()
428: .getDeclaredMethod(
429: setMethod.getName(),
430: setMethod
431: .getParameterTypes());
432: Object retval = getMethod.invoke(
433: m_targets[0], new Object[] {});
434: setMethod.invoke(myBean,
435: new Object[] { retval });
436: } catch (java.lang.NoSuchMethodException e) {
437: // not all sets are beanified
438: }
439:
440: continue;
441: }
442:
443: Method addMethod = methods[methodIndex];
444: Method getsMethod = MethodHelper
445: .getAssociatedBeanGetsMethod(addMethod,
446: methods);
447: Method removeMethod = MethodHelper
448: .getAssociatedBeanRemoveMethod(addMethod,
449: methods);
450:
451: if ((getsMethod != null) && (removeMethod != null)) {
452: try {
453: addMethod = myBean
454: .getClass()
455: .getDeclaredMethod(
456: addMethod.getName(),
457: addMethod
458: .getParameterTypes());
459: ArrayList list = (ArrayList) getsMethod
460: .invoke(m_targets[0],
461: new Object[] {});
462: for (int i = 0; i < list.size(); i++) {
463: addMethod.invoke(myBean,
464: new Object[] { list.get(i) });
465: }
466: } catch (java.lang.NoSuchMethodException e) {
467: // not all sets are beanified
468: }
469: }
470: }
471: }
472:
473: return myBean;
474: } catch (Exception e) {
475: throw new InternalException("Target( " + m_objectId
476: + " ) Caught during get bean: " + e, e);
477: }
478: }
479:
480: public void setXML(String xml) throws Exception {
481: m_cluster.setModificationTime(System.currentTimeMillis());
482:
483: synchronized (this ) {
484: com.thoughtworks.xstream.XStream xstream = new com.thoughtworks.xstream.XStream();
485: m_targets = (org.myoodb.MyOodbLocal[]) xstream.fromXML(xml);
486:
487: for (int i = 0; i < m_targets.length; i++) {
488: m_targets[i].setContainer(this );
489: }
490: }
491: }
492:
493: public String getXML() {
494: m_cluster.setModificationTime(System.currentTimeMillis());
495:
496: try {
497: synchronized (this ) {
498: if (m_targets != null) {
499: for (int i = 0; i < m_targets.length; i++) {
500: ((MyOodbLocal) m_targets[i])
501: .setConvertFlag(false);
502: }
503: }
504:
505: com.thoughtworks.xstream.XStream xstream = new com.thoughtworks.xstream.XStream();
506: return xstream.toXML(m_targets);
507: }
508: } catch (Exception e) {
509: throw new InternalException("Target( " + m_objectId
510: + " ) Caught during get xml: " + e, e);
511: }
512: }
513:
514: public MyOodbProxy getProxy() {
515: m_cluster.setModificationTime(System.currentTimeMillis());
516:
517: try {
518: if (m_myProxy == null) {
519: synchronized (MethodHelper.CLASS_TABLE) {
520: if (m_myProxy == null) {
521: String proxyName = m_targets[0].getClass()
522: .getName();
523: proxyName = proxyName.substring(0, proxyName
524: .length()
525: - IMPL_NAME_SUFFIX.length())
526: + PROXY_NAME_SUFFIX;
527:
528: Class classType = MyOodbManager.getTheManager()
529: .getClassManager().getClass(proxyName);
530: Constructor constructor = (Constructor) classType
531: .getConstructor(new Class[] {
532: Identifier.class,
533: AbstractDatabase.class });
534:
535: Object[] args = new Object[] {
536: getObjectIdentifier(),
537: MyOodbManager.getTheManager()
538: .getDatabase() };
539: m_myProxy = (MyOodbProxy) constructor
540: .newInstance(args);
541: }
542: }
543: }
544:
545: return m_myProxy;
546: } catch (Exception e) {
547: throw new InternalException("Target( " + m_objectId
548: + " ) Caught during set proxy: " + e, e);
549: }
550: }
551:
552: public int hashCode() {
553: return getObjectIdentifier().hashCode();
554: }
555:
556: public boolean equals(Object obj) {
557: if (this == obj) {
558: return true;
559: } else if (obj instanceof AbstractObjectContainer) {
560: return getObjectIdentifier().equals(
561: ((AbstractObjectContainer) obj)
562: .getObjectIdentifier());
563: } else {
564: return false;
565: }
566: }
567:
568: public String toString() {
569: return "ObjectContainer ( " + getObjectIdentifier() + " )";
570: }
571:
572: public void writeExternal(ObjectOutput out) throws IOException {
573: out.writeByte((byte) m_state);
574: out.writeObject(m_objectId);
575:
576: if (m_targets != null) {
577: for (int i = 0; i < m_targets.length; i++) {
578: ((MyOodbLocal) m_targets[i]).setConvertFlag(false);
579: }
580: }
581:
582: // TODO: synchronize on this since java object streaming is not atomic (see invokeMethod)
583: out.writeObject(m_targets);
584: }
585:
586: public void readExternal(ObjectInput in) throws IOException,
587: ClassNotFoundException {
588: m_state = (int) in.readByte();
589: m_objectId = (Identifier) in.readObject();
590: m_targets = (MyOodbLocal[]) in.readObject();
591:
592: // XXX: null m_targets is valid for transaction rollback ( clean up on verify )
593: MyOodbManager manager = MyOodbManager.getTheManager();
594: if ((m_targets == null)
595: && ((manager == null) || (manager.getStoreManager()
596: .isVerifyingDatabase() == false))) {
597: return;
598: }
599:
600: for (int i = 0; i < m_targets.length; i++) {
601: m_targets[i].setContainer(this);
602: }
603: }
604: }
|