001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software 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 GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.ejb.plugins.cmp.jdbc.bridge;
023:
024: import java.util.Collection;
025: import java.util.ConcurrentModificationException;
026: import java.util.ArrayList;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Set;
030: import java.util.HashSet;
031: import javax.ejb.EJBException;
032: import javax.ejb.EJBLocalObject;
033: import javax.ejb.NoSuchObjectLocalException;
034:
035: import org.jboss.ejb.EntityEnterpriseContext;
036: import org.jboss.ejb.LocalProxyFactory;
037:
038: /**
039: * This is the relationship set. An instance of this class
040: * is returned when collection valued cmr field is accessed.
041: * See the EJB 2.0 specification for a more detailed description
042: * or the responsibilities of this class.
043: *
044: * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
045: * @version $Revision: 57209 $
046: */
047: public class RelationSet implements Set {
048: private JDBCCMRFieldBridge cmrField;
049: private EntityEnterpriseContext ctx;
050: private List[] setHandle;
051: private boolean readOnly;
052: private Class relatedLocalInterface;
053:
054: //
055: // Most of this class is a boring wrapper arround the id set.
056: // The only interesting hitch is the setHandle. This class doesn't
057: // have a direct referance to the related id set, it has a referance
058: // to a referance to the set. When the transaction is completed the
059: // CMR field sets my referance to the set to null, so that I know that
060: // this set is no longer valid. See the ejb spec for more info.
061: //
062: public RelationSet(JDBCCMRFieldBridge cmrField,
063: EntityEnterpriseContext ctx, List[] setHandle,
064: boolean readOnly) {
065:
066: this .cmrField = cmrField;
067: this .ctx = ctx;
068: this .setHandle = setHandle;
069: this .readOnly = readOnly;
070: relatedLocalInterface = cmrField.getRelatedLocalInterface();
071: }
072:
073: private List getIdList() {
074: if (setHandle[0] == null) {
075: throw new IllegalStateException(
076: "A CMR collection may only be used "
077: + "within the transction in which it was created");
078: }
079: return setHandle[0];
080: }
081:
082: public int size() {
083: List idList = getIdList();
084: return idList.size();
085: }
086:
087: public boolean isEmpty() {
088: List idList = getIdList();
089: return idList.isEmpty();
090: }
091:
092: public boolean add(Object o) {
093: if (o == null) {
094: throw new IllegalArgumentException(
095: "Null cannot be added to a CMR "
096: + "relationship collection");
097: }
098:
099: checkForPKChange();
100:
101: List idList = getIdList();
102: if (readOnly) {
103: throw new EJBException(
104: "This collection is a read-only snapshot");
105: }
106:
107: if (cmrField.isReadOnly()) {
108: throw new EJBException("Field is read-only: "
109: + cmrField.getFieldName());
110: }
111:
112: if (!relatedLocalInterface.isInstance(o)) {
113: String msg = "Object must be an instance of "
114: + relatedLocalInterface.getName()
115: + ", but is an isntance of [";
116: Class[] classes = o.getClass().getInterfaces();
117: for (int i = 0; i < classes.length; i++) {
118: if (i > 0)
119: msg += ", ";
120: msg += classes[i].getName();
121: }
122: msg += "]";
123: throw new IllegalArgumentException(msg);
124: }
125:
126: Object id = getPrimaryKey((EJBLocalObject) o);
127: if (idList.contains(id)) {
128: return false;
129: }
130: cmrField.createRelationLinks(ctx, id);
131: return true;
132: }
133:
134: public boolean addAll(Collection c) {
135: if (readOnly) {
136: throw new EJBException(
137: "This collection is a read-only snapshot");
138: }
139: if (cmrField.isReadOnly()) {
140: throw new EJBException("Field is read-only: "
141: + cmrField.getFieldName());
142: }
143:
144: if (c == null) {
145: return false;
146: }
147:
148: boolean isModified = false;
149:
150: Iterator iterator = (new HashSet(c)).iterator();
151: while (iterator.hasNext()) {
152: isModified = add(iterator.next()) || isModified;
153: }
154: return isModified;
155: }
156:
157: public boolean remove(Object o) {
158: List idList = getIdList();
159: if (readOnly) {
160: throw new EJBException(
161: "This collection is a read-only snapshot");
162: }
163: if (cmrField.isReadOnly()) {
164: throw new EJBException("Field is read-only: "
165: + cmrField.getFieldName());
166: }
167:
168: checkForPKChange();
169:
170: if (!relatedLocalInterface.isInstance(o)) {
171: throw new IllegalArgumentException(
172: "Object must be an instance of "
173: + relatedLocalInterface.getName());
174: }
175:
176: Object id = getPrimaryKey((EJBLocalObject) o);
177: if (!idList.contains(id)) {
178: return false;
179: }
180: cmrField.destroyRelationLinks(ctx, id);
181: return true;
182: }
183:
184: public boolean removeAll(Collection c) {
185: if (readOnly) {
186: throw new EJBException(
187: "This collection is a read-only snapshot");
188: }
189: if (cmrField.isReadOnly()) {
190: throw new EJBException("Field is read-only: "
191: + cmrField.getFieldName());
192: }
193:
194: if (c == null) {
195: return false;
196: }
197:
198: boolean isModified = false;
199:
200: Iterator iterator = (new HashSet(c)).iterator();
201: while (iterator.hasNext()) {
202: isModified = remove(iterator.next()) || isModified;
203: }
204: return isModified;
205: }
206:
207: public void clear() {
208: checkForPKChange();
209:
210: List idList = getIdList();
211: if (readOnly) {
212: throw new EJBException(
213: "This collection is a read-only snapshot");
214: }
215: if (cmrField.isReadOnly()) {
216: throw new EJBException("Field is read-only: "
217: + cmrField.getFieldName());
218: }
219:
220: Iterator iterator = (new ArrayList(idList)).iterator();
221: while (iterator.hasNext()) {
222: cmrField.destroyRelationLinks(ctx, iterator.next());
223: }
224: }
225:
226: public boolean retainAll(Collection c) {
227: List idList = getIdList();
228: if (readOnly) {
229: throw new EJBException(
230: "This collection is a read-only snapshot");
231: }
232: if (cmrField.isReadOnly()) {
233: throw new EJBException("Field is read-only: "
234: + cmrField.getFieldName());
235: }
236:
237: checkForPKChange();
238:
239: if (c == null) {
240: if (idList.size() == 0) {
241: return false;
242: }
243: clear();
244: return true;
245: }
246:
247: // get a set of the argument collection's ids
248: List argIds = new ArrayList();
249: Iterator iterator = c.iterator();
250: while (iterator.hasNext()) {
251: EJBLocalObject localObject = (EJBLocalObject) iterator
252: .next();
253: Object relatedId = getPrimaryKey(localObject);
254: argIds.add(relatedId);
255: }
256:
257: boolean isModified = false;
258:
259: iterator = (new ArrayList(idList)).iterator();
260: while (iterator.hasNext()) {
261: Object id = iterator.next();
262: if (!argIds.contains(id)) {
263: cmrField.destroyRelationLinks(ctx, id);
264: isModified = true;
265: }
266: }
267: return isModified;
268: }
269:
270: public boolean contains(Object o) {
271: List idList = getIdList();
272:
273: if (!relatedLocalInterface.isInstance(o)) {
274: throw new IllegalArgumentException(
275: "Object must be an instance of "
276: + relatedLocalInterface.getName());
277: }
278:
279: Object id = getPrimaryKey((EJBLocalObject) o);
280: return idList.contains(id);
281: }
282:
283: public boolean containsAll(Collection c) {
284: List idList = getIdList();
285:
286: if (c == null) {
287: return true;
288: }
289:
290: // get a set of the argument collection's ids
291: List argIds = new ArrayList();
292: Iterator iterator = c.iterator();
293: while (iterator.hasNext()) {
294: EJBLocalObject localObject = (EJBLocalObject) iterator
295: .next();
296: Object relatedId = getPrimaryKey(localObject);
297: argIds.add(relatedId);
298: }
299:
300: return idList.containsAll(argIds);
301: }
302:
303: public Object[] toArray(Object a[]) {
304: List idList = getIdList();
305:
306: Collection c = cmrField.getRelatedInvoker()
307: .getEntityLocalCollection(idList);
308: return c.toArray(a);
309: }
310:
311: public Object[] toArray() {
312: List idList = getIdList();
313: Collection c = cmrField.getRelatedInvoker()
314: .getEntityLocalCollection(idList);
315: return c.toArray();
316: }
317:
318: // Private
319:
320: private static void checkForPKChange() {
321: /**
322: * Uncomment to disallow attempts to override PK value with equal FK value
323: *
324: if(cmrField.getRelatedCMRField().allFkFieldsMappedToPkFields()) {
325: throw new IllegalStateException(
326: "Can't modify relationship: CMR field "
327: + cmrField.getRelatedEntity().getEntityName() + "." + cmrField.getRelatedCMRField().getFieldName()
328: + " has _ALL_ foreign key fields mapped to the primary key columns."
329: + " Primary key may only be set once in ejbCreate [EJB 2.0 Spec. 10.3.5].");
330: }
331: */
332: }
333:
334: // Inner
335:
336: public Iterator iterator() {
337: return new Iterator() {
338: private final Iterator idIterator = getIdList().iterator();
339: private final LocalProxyFactory localFactory = cmrField
340: .getRelatedInvoker();
341: private Object currentId;
342:
343: public boolean hasNext() {
344: verifyIteratorIsValid();
345:
346: try {
347: return idIterator.hasNext();
348: } catch (ConcurrentModificationException e) {
349: throw new IllegalStateException(
350: "Underlying collection has "
351: + "been modified");
352: }
353: }
354:
355: public Object next() {
356: verifyIteratorIsValid();
357:
358: try {
359: currentId = idIterator.next();
360: return localFactory
361: .getEntityEJBLocalObject(currentId);
362: } catch (ConcurrentModificationException e) {
363: throw new IllegalStateException(
364: "Underlying collection has "
365: + "been modified");
366: }
367: }
368:
369: public void remove() {
370: verifyIteratorIsValid();
371: if (readOnly) {
372: throw new EJBException(
373: "This collection is a read-only snapshot");
374: }
375: if (cmrField.isReadOnly()) {
376: throw new EJBException("Field is read-only: "
377: + cmrField.getFieldName());
378: }
379:
380: checkForPKChange();
381:
382: try {
383: idIterator.remove();
384: cmrField
385: .destroyRelationLinks(ctx, currentId, false);
386: } catch (ConcurrentModificationException e) {
387: throw new IllegalStateException(
388: "Underlying collection has been modified");
389: }
390: }
391:
392: private void verifyIteratorIsValid() {
393: if (setHandle[0] == null) {
394: throw new IllegalStateException(
395: "The iterator of a CMR "
396: + "collection may only be used within the transction in "
397: + "which it was created");
398: }
399: }
400: };
401: }
402:
403: public String toString() {
404: return new StringBuffer().append('[').append(
405: cmrField.getEntity().getEntityName()).append('.')
406: .append(cmrField.getFieldName()).append(':').append(
407: getIdList()).append(']').toString();
408: }
409:
410: private Object getPrimaryKey(EJBLocalObject o) {
411: try {
412: return o.getPrimaryKey();
413: } catch (NoSuchObjectLocalException e) {
414: throw new IllegalArgumentException(e.getMessage());
415: }
416: }
417: }
|