1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.ejb.plugins.cmp.jdbc.bridge;
23
24 import java.sql.PreparedStatement;
25 import java.sql.ResultSet;
26
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.Collections;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Arrays;
35 import java.util.NoSuchElementException;
36 import java.rmi.RemoteException;
37
38 import javax.ejb.EJBException;
39 import javax.ejb.RemoveException;
40 import javax.sql.DataSource;
41 import javax.naming.InitialContext;
42 import javax.naming.NamingException;
43
44 import org.jboss.deployment.DeploymentException;
45 import org.jboss.ejb.EntityEnterpriseContext;
46
47 import org.jboss.ejb.plugins.cmp.jdbc.JDBCContext;
48 import org.jboss.ejb.plugins.cmp.jdbc.JDBCStoreManager;
49 import org.jboss.ejb.plugins.cmp.jdbc.SQLUtil;
50 import org.jboss.ejb.plugins.cmp.jdbc.LockingStrategy;
51 import org.jboss.ejb.plugins.cmp.jdbc.JDBCTypeFactory;
52 import org.jboss.ejb.plugins.cmp.jdbc.JDBCEntityPersistenceStore;
53
54 import org.jboss.ejb.plugins.cmp.bridge.EntityBridgeInvocationHandler;
55 import org.jboss.ejb.plugins.cmp.bridge.FieldBridge;
56
57 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCAuditMetaData;
58 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCEntityMetaData;
59 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCCMPFieldMetaData;
60 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCQueryMetaData;
61 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCRelationshipRoleMetaData;
62 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCOptimisticLockingMetaData;
63 import org.jboss.ejb.plugins.cmp.jdbc.metadata.JDBCReadAheadMetaData;
64 import org.jboss.proxy.compiler.Proxies;
65 import org.jboss.proxy.compiler.InvocationHandler;
66 import org.jboss.logging.Logger;
67
68
69 /**
70 * JDBCEntityBridge follows the Bridge pattern [Gamma et. al, 1995].
71 * The main job of this class is to construct the bridge from entity meta data.
72 *
73 * Life-cycle:
74 * Undefined. Should be tied to CMPStoreManager.
75 *
76 * Multiplicity:
77 * One per cmp entity bean type.
78 *
79 * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a>
80 * @author <a href="mailto:loubyansky@ua.fm">Alex Loubyansky</a>
81 * @author <a href="mailto:heiko.rupp@cellent.de">Heiko W. Rupp</a>
82 * @version $Revision: 43495 $
83 */
84 public class JDBCEntityBridge implements JDBCAbstractEntityBridge
85 {
86 public final static byte LOADED = 1;
87 public final static byte LOAD_REQUIRED = 2;
88 public final static byte DIRTY = 4;
89 public final static byte CHECK_DIRTY = 8;
90 public final static byte LOCKED = 16;
91 public final static byte ADD_TO_SET_ON_UPDATE = 32;
92 public final static byte ADD_TO_WHERE_ON_UPDATE = 64;
93
94 private static final String DEFAULT_LOADGROUP_NAME = "*";
95
96 private JDBCEntityMetaData metadata;
97 private JDBCStoreManager manager;
98 private DataSource dataSource;
99 private String qualifiedTableName;
100 private String tableName;
101
102 /** primary key fields (not added to cmpFields) */
103 private final String primaryKeyFieldName;
104 private final Class primaryKeyClass;
105 private JDBCCMPFieldBridge[] primaryKeyFields;
106 /** CMP fields */
107 private JDBCCMPFieldBridge[] cmpFields;
108 /** CMR fields */
109 private JDBCCMRFieldBridge[] cmrFields;
110 /** table fields */
111 private JDBCCMPFieldBridge[] tableFields;
112
113 /** used for optimistic locking. (added to cmpFields) */
114 private JDBCCMPFieldBridge versionField;
115
116 // Audit fields (added to cmpFields)
117 private JDBCCMPFieldBridge createdPrincipalField;
118 private JDBCCMPFieldBridge createdTimeField;
119 private JDBCCMPFieldBridge updatedPrincipalField;
120 private JDBCCMPFieldBridge updatedTimeField;
121
122 private Map selectorsByMethod;
123
124 /** Load group is a boolean array with tableFields.length elements. True means the element is in the group. */
125 private Map loadGroupMasks;
126 private List lazyLoadGroupMasks;
127 private boolean[] eagerLoadGroupMask;
128 private boolean[] defaultLockGroupMask;
129
130 private int jdbcContextSize;
131
132 private final Logger log;
133
134 public JDBCEntityBridge(JDBCEntityMetaData metadata, JDBCStoreManager manager)
135 throws DeploymentException
136 {
137 this.metadata = metadata;
138 this.manager = manager;
139 primaryKeyFieldName = metadata.getPrimaryKeyFieldName();
140 primaryKeyClass = metadata.getPrimaryKeyClass();
141 log = Logger.getLogger(this.getClass().getName() + "." + metadata.getName());
142 }
143
144 public void init() throws DeploymentException
145 {
146 try
147 {
148 InitialContext ic = new InitialContext();
149 dataSource = (DataSource)ic.lookup(metadata.getDataSourceName());
150 }
151 catch(NamingException e)
152 {
153 throw new DeploymentException("Error: can't find data source: " +
154 metadata.getDataSourceName(), e);
155 }
156
157 qualifiedTableName = SQLUtil.fixTableName(metadata.getDefaultTableName(), dataSource);
158 int dotIndex = qualifiedTableName.indexOf('.');
159 tableName = dotIndex == -1 ? qualifiedTableName : qualifiedTableName.substring(dotIndex + 1);
160
161 // CMP fields
162 loadCMPFields(metadata);
163
164 // CMR fields
165 loadCMRFields(metadata);
166
167 // create locking field
168 JDBCOptimisticLockingMetaData lockMetaData = metadata.getOptimisticLocking();
169 if(lockMetaData != null && lockMetaData.getLockingField() != null)
170 {
171 Integer strategy = lockMetaData.getLockingStrategy();
172 JDBCCMPFieldMetaData versionMD = lockMetaData.getLockingField();
173
174 versionField = getCMPFieldByName(versionMD.getFieldName());
175 boolean hidden = versionField == null;
176 if(strategy == JDBCOptimisticLockingMetaData.VERSION_COLUMN_STRATEGY)
177 {
178 if(hidden)
179 versionField = new JDBCLongVersionFieldBridge(manager, versionMD);
180 else
181 versionField = new JDBCLongVersionFieldBridge((JDBCCMP2xFieldBridge)versionField);
182 }
183 else if(strategy == JDBCOptimisticLockingMetaData.TIMESTAMP_COLUMN_STRATEGY)
184 {
185 if(hidden)
186 versionField = new JDBCTimestampVersionFieldBridge(manager, versionMD);
187 else
188 versionField = new JDBCTimestampVersionFieldBridge((JDBCCMP2xFieldBridge)versionField);
189 }
190 else if(strategy == JDBCOptimisticLockingMetaData.KEYGENERATOR_COLUMN_STRATEGY)
191 {
192 if(hidden)
193 versionField = new JDBCKeyGenVersionFieldBridge(
194 manager, versionMD, lockMetaData.getKeyGeneratorFactory());
195 else
196 versionField = new JDBCKeyGenVersionFieldBridge(
197 (JDBCCMP2xFieldBridge)versionField, lockMetaData.getKeyGeneratorFactory());
198 }
199
200 if(hidden)
201 addCMPField(versionField);
202 else
203 tableFields[versionField.getTableIndex()] = versionField;
204 }
205
206 // audit fields
207 JDBCAuditMetaData auditMetaData = metadata.getAudit();
208 if(auditMetaData != null)
209 {
210 JDBCCMPFieldMetaData auditField = auditMetaData.getCreatedPrincipalField();
211 if(auditField != null)
212 {
213 createdPrincipalField = getCMPFieldByName(auditField.getFieldName());
214 if(createdPrincipalField == null)
215 {
216 createdPrincipalField = new JDBCCMP2xFieldBridge(manager, auditField);
217 addCMPField(createdPrincipalField);
218 }
219 }
220 else
221 {
222 createdPrincipalField = null;
223 }
224
225 auditField = auditMetaData.getCreatedTimeField();
226 if(auditField != null)
227 {
228 createdTimeField = getCMPFieldByName(auditField.getFieldName());
229 if(createdTimeField == null)
230 {
231 createdTimeField = new JDBCCMP2xFieldBridge(manager, auditField, JDBCTypeFactory.EQUALS, false);
232 addCMPField(createdTimeField);
233 }
234 else
235 {
236 // just to override state factory and check-dirty-after-get
237 createdTimeField = new JDBCCMP2xFieldBridge(
238 (JDBCCMP2xFieldBridge)createdTimeField, JDBCTypeFactory.EQUALS, false);
239 tableFields[createdTimeField.getTableIndex()] = createdTimeField;
240 }
241 }
242 else
243 {
244 createdTimeField = null;
245 }
246
247 auditField = auditMetaData.getUpdatedPrincipalField();
248 if(auditField != null)
249 {
250 updatedPrincipalField = getCMPFieldByName(auditField.getFieldName());
251 if(updatedPrincipalField == null)
252 {
253 updatedPrincipalField = new JDBCCMP2xUpdatedPrincipalFieldBridge(manager, auditField);
254 addCMPField(updatedPrincipalField);
255 }
256 else
257 {
258 updatedPrincipalField = new JDBCCMP2xUpdatedPrincipalFieldBridge(
259 (JDBCCMP2xFieldBridge)updatedPrincipalField);
260 tableFields[updatedPrincipalField.getTableIndex()] = updatedPrincipalField;
261 }
262 }
263 else
264 {
265 updatedPrincipalField = null;
266 }
267
268 auditField = auditMetaData.getUpdatedTimeField();
269 if(auditField != null)
270 {
271 updatedTimeField = getCMPFieldByName(auditField.getFieldName());
272 if(updatedTimeField == null)
273 {
274 updatedTimeField = new JDBCCMP2xUpdatedTimeFieldBridge(manager, auditField);
275 addCMPField(updatedTimeField);
276 }
277 else
278 {
279 updatedTimeField = new JDBCCMP2xUpdatedTimeFieldBridge((JDBCCMP2xFieldBridge)updatedTimeField);
280 tableFields[updatedTimeField.getTableIndex()] = updatedTimeField;
281 }
282 }
283 else
284 {
285 updatedTimeField = null;
286 }
287 }
288
289 // ejbSelect methods
290 loadSelectors(metadata);
291 }
292
293 public void resolveRelationships() throws DeploymentException
294 {
295 for(int i = 0; i < cmrFields.length; ++i)
296 cmrFields[i].resolveRelationship();
297
298 // load groups: cannot be created until relationships have
299 // been resolved because loadgroups must check for foreign keys
300 loadLoadGroups(metadata);
301 loadEagerLoadGroup(metadata);
302 loadLazyLoadGroups(metadata);
303 }
304
305 /**
306 * The third phase of deployment. The method is called when relationships are already resolved.
307 * @throws DeploymentException
308 */
309 public void start() throws DeploymentException
310 {
311 for(int i = 0; i < cmrFields.length; ++i)
312 {
313 cmrFields[i].start();
314 }
315 }
316
317 public boolean removeFromRelations(EntityEnterpriseContext ctx, Object[] oldRelations)
318 {
319 boolean removed = false;
320 for(int i = 0; i < cmrFields.length; ++i)
321 {
322 if(cmrFields[i].removeFromRelations(ctx, oldRelations))
323 removed = true;
324 }
325 return removed;
326 }
327
328 public void cascadeDelete(EntityEnterpriseContext ctx, Map oldRelations)
329 throws RemoveException, RemoteException
330 {
331 for(int i = 0; i < cmrFields.length; ++i)
332 {
333 JDBCCMRFieldBridge cmrField = cmrFields[i];
334 Object value = oldRelations.get(cmrField);
335 if(value != null)
336 cmrField.cascadeDelete(ctx, (List)value);
337 }
338 }
339
340 public String getEntityName()
341 {
342 return metadata.getName();
343 }
344
345 public String getAbstractSchemaName()
346 {
347 return metadata.getAbstractSchemaName();
348 }
349
350 public Class getRemoteInterface()
351 {
352 return metadata.getRemoteClass();
353 }
354
355 public Class getLocalInterface()
356 {
357 return metadata.getLocalClass();
358 }
359
360 public JDBCEntityMetaData getMetaData()
361 {
362 return metadata;
363 }
364
365 public JDBCEntityPersistenceStore getManager()
366 {
367 return manager;
368 }
369
370 /**
371 * Returns the datasource for this entity.
372 */
373 public DataSource getDataSource()
374 {
375 return dataSource;
376 }
377
378 public String getTableName()
379 {
380 return tableName;
381 }
382
383 public String getQualifiedTableName()
384 {
385 return qualifiedTableName;
386 }
387
388 public Class getPrimaryKeyClass()
389 {
390 return primaryKeyClass;
391 }
392
393 public int getListCacheMax()
394 {
395 return metadata.getListCacheMax();
396 }
397
398 public int getFetchSize()
399 {
400 return metadata.getFetchSize();
401 }
402
403 public Object createPrimaryKeyInstance()
404 {
405 if(primaryKeyFieldName == null)
406 {
407 try
408 {
409 return primaryKeyClass.newInstance();
410 }
411 catch(Exception e)
412 {
413 throw new EJBException("Error creating primary key instance: ", e);
414 }
415 }
416 return null;
417 }
418
419 public JDBCFieldBridge[] getPrimaryKeyFields()
420 {
421 return primaryKeyFields;
422 }
423
424 /**
425 * This method is called only at deployment time, not called at runtime.
426 * @return the list of all the fields.
427 */
428 public List getFields()
429 {
430 int fieldsTotal = primaryKeyFields.length + cmpFields.length + cmrFields.length;
431 JDBCFieldBridge[] fields = new JDBCFieldBridge[fieldsTotal];
432 int position = 0;
433 // primary key fields
434 System.arraycopy(primaryKeyFields, 0, fields, position, primaryKeyFields.length);
435 position += primaryKeyFields.length;
436 // cmp fields
437 System.arraycopy(cmpFields, 0, fields, position, cmpFields.length);
438 position += cmpFields.length;
439 // cmr fields
440 System.arraycopy(cmrFields, 0, fields, position, cmrFields.length);
441 return Arrays.asList(fields);
442 }
443
444 public FieldBridge getFieldByName(String name)
445 {
446 FieldBridge field = null;
447 for(int i = 0; i < primaryKeyFields.length; ++i)
448 {
449 JDBCCMPFieldBridge primaryKeyField = primaryKeyFields[i];
450 if(primaryKeyField.getFieldName().equals(name))
451 {
452 field = primaryKeyField;
453 break;
454 }
455 }
456 if(field == null)
457 {
458 field = getCMPFieldByName(name);
459 }
460 if(field == null)
461 {
462 field = getCMRFieldByName(name);
463 }
464 return field;
465 }
466
467 public boolean[] getEagerLoadMask()
468 {
469 return eagerLoadGroupMask;
470 }
471
472 public Iterator getLazyLoadGroupMasks()
473 {
474 return lazyLoadGroupMasks.iterator();
475 }
476
477 public boolean[] getLoadGroupMask(String name)
478 {
479 boolean[] mask = (boolean[])loadGroupMasks.get(name);
480 if(mask == null)
481 {
482 throw new IllegalStateException(
483 "Load group '" + name + "' is not defined. Defined load groups: " + loadGroupMasks.keySet()
484 );
485 }
486 return mask;
487 }
488
489 public FieldIterator getLoadIterator(JDBCCMPFieldBridge requiredField,
490 JDBCReadAheadMetaData readahead,
491 EntityEnterpriseContext ctx)
492 {
493 boolean[] loadGroup;
494 if(requiredField == null)
495 {
496 if(readahead != null && !readahead.isNone())
497 {
498 if(log.isTraceEnabled())
499 {
500 log.trace("Eager-load for entity: readahead=" + readahead);
501 }
502 loadGroup = getLoadGroupMask(readahead.getEagerLoadGroup());
503 }
504 else
505 {
506 if(log.isTraceEnabled())
507 {
508 log.trace("Default eager-load for entity: readahead=" + readahead);
509 }
510 loadGroup = eagerLoadGroupMask;
511 }
512 }
513 else
514 {
515 loadGroup = new boolean[tableFields.length];
516 int requiredInd = requiredField.getTableIndex();
517 loadGroup[requiredInd] = true;
518 for(Iterator groups = lazyLoadGroupMasks.iterator(); groups.hasNext();)
519 {
520 boolean[] lazyGroup = (boolean[])groups.next();
521 if(lazyGroup[requiredInd])
522 {
523 for(int i = 0; i < loadGroup.length; ++i)
524 loadGroup[i] = loadGroup[i] || lazyGroup[i];
525 }
526 }
527 }
528
529 FieldIterator loadIter;
530 if(loadGroup != null)
531 {
532 // filter
533 int fieldsToLoad = 0;
534 EntityState entityState = getEntityState(ctx);
535 for(int i = 0; i < tableFields.length; ++i)
536 {
537 JDBCCMPFieldBridge field = tableFields[i];
538 if(loadGroup[i] && !field.isPrimaryKeyMember() && !field.isLoaded(ctx))
539 {
540 entityState.setLoadRequired(i);
541 ++fieldsToLoad;
542 }
543 }
544 loadIter = (fieldsToLoad > 0 ? entityState.getLoadIterator(ctx) : EMPTY_FIELD_ITERATOR);
545 }
546 else
547 {
548 loadIter = EMPTY_FIELD_ITERATOR;
549 }
550 return loadIter;
551 }
552
553 /**
554 * @param name CMP field name
555 * @return JDBCCMPFieldBridge instance or null if no field found.
556 */
557 public JDBCCMPFieldBridge getCMPFieldByName(String name)
558 {
559 for(int i = 0; i < primaryKeyFields.length; ++i)
560 {
561 JDBCCMPFieldBridge cmpField = primaryKeyFields[i];
562 if(cmpField.getFieldName().equals(name))
563 return cmpField;
564 }
565 for(int i = 0; i < cmpFields.length; ++i)
566 {
567 JDBCCMPFieldBridge cmpField = cmpFields[i];
568 if(cmpField.getFieldName().equals(name))
569 return cmpField;
570 }
571 return null;
572 }
573
574 public JDBCAbstractCMRFieldBridge[] getCMRFields()
575 {
576 return cmrFields;
577 }
578
579 public JDBCCMRFieldBridge getCMRFieldByName(String name)
580 {
581 for(int i = 0; i < cmrFields.length; ++i)
582 {
583 JDBCCMRFieldBridge cmrField = cmrFields[i];
584 if(cmrField.getFieldName().equals(name))
585 return cmrField;
586 }
587 return null;
588 }
589
590 public JDBCCMPFieldBridge getVersionField()
591 {
592 return versionField;
593 }
594
595 public JDBCCMPFieldBridge getCreatedPrincipalField()
596 {
597 return createdPrincipalField;
598 }
599
600 public JDBCCMPFieldBridge getCreatedTimeField()
601 {
602 return createdTimeField;
603 }
604
605 public JDBCCMPFieldBridge getUpdatedPrincipalField()
606 {
607 return updatedPrincipalField;
608 }
609
610 public JDBCCMPFieldBridge getUpdatedTimeField()
611 {
612 return updatedTimeField;
613 }
614
615 public Collection getSelectors()
616 {
617 return selectorsByMethod.values();
618 }
619
620 public void initInstance(EntityEnterpriseContext ctx)
621 {
622 for(int i = 0; i < tableFields.length; ++i)
623 tableFields[i].initInstance(ctx);
624 //for(int i = 0; i < primaryKeyFields.length; ++i)
625 // primaryKeyFields[i].initInstance(ctx);
626 //for(int i = 0; i < cmpFields.length; ++i)
627 // cmpFields[i].initInstance(ctx);
628 for(int i = 0; i < cmrFields.length; ++i)
629 {
630 JDBCCMRFieldBridge cmrField = cmrFields[i];
631 cmrField.initInstance(ctx);
632 }
633 }
634
635 public static boolean isEjbCreateDone(EntityEnterpriseContext ctx)
636 {
637 return getEntityState(ctx).ejbCreateDone;
638 }
639
640 public static void setCreated(EntityEnterpriseContext ctx)
641 {
642 getEntityState(ctx).setCreated();
643 }
644
645 public static void setEjbCreateDone(EntityEnterpriseContext ctx)
646 {
647 getEntityState(ctx).ejbCreateDone = true;
648 }
649
650 /**
651 * This method is used to determined whether the instance was modified.
652 * NOTE, even if the method returns true the isStoreRequired for this same instance
653 * might return false, e.g. a CMR field that doesn't have a foreign key was modified.
654 * @param ctx
655 * @return
656 */
657 public boolean isModified(EntityEnterpriseContext ctx)
658 {
659 boolean invalidateCache = false;
660 final EntityState entityState = getEntityState(ctx);
661 if(entityState.isCreated())
662 {
663 invalidateCache = areCmpFieldsDirty(ctx, entityState);
664 if(!invalidateCache)
665 {
666 for(int i = 0; i < cmrFields.length; ++i)
667 {
668 if(cmrFields[i].invalidateCache(ctx))
669 {
670 invalidateCache = true;
671 break;
672 }
673 }
674 }
675 }
676 return invalidateCache;
677 }
678
679 public boolean isStoreRequired(EntityEnterpriseContext ctx)
680 {
681 boolean modified = false;
682 final EntityState entityState = getEntityState(ctx);
683 if(entityState.isCreated())
684 {
685 modified = areCmpFieldsDirty(ctx, entityState);
686 if(!modified)
687 {
688 for(int i = 0; i < cmrFields.length; ++i)
689 {
690 if(cmrFields[i].isDirty(ctx))
691 {
692 modified = true;
693 break;
694 }
695 }
696 }
697 }
698 return modified;
699 }
700
701 private boolean areCmpFieldsDirty(final EntityEnterpriseContext ctx,
702 final EntityState entityState)
703 {
704 for(int i = 0; i < tableFields.length; ++i)
705 {
706 final JDBCCMPFieldBridge field = tableFields[i];
707 if(entityState.isCheckDirty(i) && field.isDirty(ctx))
708 {
709 return true;
710 }
711 }
712 return false;
713 }
714
715 public FieldIterator getDirtyIterator(EntityEnterpriseContext ctx)
716 {
717 int dirtyFields = 0;
718 final EntityState entityState = getEntityState(ctx);
719 for(int i = 0; i < tableFields.length; ++i)
720 {
721 JDBCCMPFieldBridge field = tableFields[i];
722 if(entityState.isCheckDirty(i) && field.isDirty(ctx))
723 {
724 entityState.setUpdateRequired(i);
725 ++dirtyFields;
726 }
727 }
728
729 return dirtyFields > 0 ? getEntityState(ctx).getDirtyIterator(ctx) : EMPTY_FIELD_ITERATOR;
730 }
731
732 public boolean hasLockedFields(EntityEnterpriseContext ctx)
733 {
734 return getEntityState(ctx).hasLockedFields();
735 }
736
737 public FieldIterator getLockedIterator(EntityEnterpriseContext ctx)
738 {
739 return getEntityState(ctx).getLockedIterator(ctx);
740 }
741
742 public void initPersistenceContext(EntityEnterpriseContext ctx)
743 {
744 // If we have an EJB 2.0 dynaymic proxy,
745 // notify the handler of the assigned context.
746 Object instance = ctx.getInstance();
747 if(instance instanceof Proxies.ProxyTarget)
748 {
749 InvocationHandler handler = ((Proxies.ProxyTarget)instance).getInvocationHandler();
750 if(handler instanceof EntityBridgeInvocationHandler)
751 ((EntityBridgeInvocationHandler)handler).setContext(ctx);
752 }
753 ctx.setPersistenceContext(new JDBCContext(jdbcContextSize, new EntityState()));
754 }
755
756 /**
757 * This is only called in commit option B
758 */
759 public void resetPersistenceContext(EntityEnterpriseContext ctx)
760 {
761 for(int i = 0; i < primaryKeyFields.length; ++i)
762 primaryKeyFields[i].resetPersistenceContext(ctx);
763 for(int i = 0; i < cmpFields.length; ++i)
764 cmpFields[i].resetPersistenceContext(ctx);
765 for(int i = 0; i < cmrFields.length; ++i)
766 cmrFields[i].resetPersistenceContext(ctx);
767 }
768
769
770 public static void destroyPersistenceContext(EntityEnterpriseContext ctx)
771 {
772 // If we have an EJB 2.0 dynaymic proxy,
773 // notify the handler of the assigned context.
774 Object instance = ctx.getInstance();
775 if(instance instanceof Proxies.ProxyTarget)
776 {
777 InvocationHandler handler = ((Proxies.ProxyTarget)instance).getInvocationHandler();
778 if(handler instanceof EntityBridgeInvocationHandler)
779 ((EntityBridgeInvocationHandler)handler).setContext(null);
780 }
781 ctx.setPersistenceContext(null);
782 }
783
784 //
785 // Commands to handle primary keys
786 //
787
788 public int setPrimaryKeyParameters(PreparedStatement ps, int parameterIndex, Object primaryKey)
789 {
790 for(int i = 0; i < primaryKeyFields.length; ++i)
791 parameterIndex = primaryKeyFields[i].setPrimaryKeyParameters(ps, parameterIndex, primaryKey);
792 return parameterIndex;
793 }
794
795 public int loadPrimaryKeyResults(ResultSet rs, int parameterIndex, Object[] pkRef)
796 {
797 pkRef[0] = createPrimaryKeyInstance();
798 for(int i = 0; i < primaryKeyFields.length; ++i)
799 parameterIndex = primaryKeyFields[i].loadPrimaryKeyResults(rs, parameterIndex, pkRef);
800 return parameterIndex;
801 }
802
803 public Object extractPrimaryKeyFromInstance(EntityEnterpriseContext ctx)
804 {
805 try
806 {
807 Object pk = null;
808 for(int i = 0; i < primaryKeyFields.length; ++i)
809 {
810 JDBCCMPFieldBridge pkField = primaryKeyFields[i];
811 Object fieldValue = pkField.getInstanceValue(ctx);
812
813 // updated pk object with return form set primary key value to
814 // handle single valued non-composit pks and more complicated behivors.
815 pk = pkField.setPrimaryKeyValue(pk, fieldValue);
816 }
817 return pk;
818 }
819 catch(EJBException e)
820 {
821 // to avoid double wrap of EJBExceptions
822 throw e;
823 }
824 catch(Exception e)
825 {
826 // Non recoverable internal exception
827 throw new EJBException("Internal error extracting primary key from " +
828 "instance", e);
829 }
830 }
831
832 public void injectPrimaryKeyIntoInstance(EntityEnterpriseContext ctx, Object pk)
833 {
834 for(int i = 0; i < primaryKeyFields.length; ++i)
835 {
836 JDBCCMPFieldBridge pkField = primaryKeyFields[i];
837 Object fieldValue = pkField.getPrimaryKeyValue(pk);
838 pkField.setInstanceValue(ctx, fieldValue);
839 }
840 }
841
842 int getNextJDBCContextIndex()
843 {
844 return jdbcContextSize++;
845 }
846
847 int addTableField(JDBCCMPFieldBridge field)
848 {
849 JDBCCMPFieldBridge[] tmpFields = tableFields;
850 if(tableFields == null)
851 {
852 tableFields = new JDBCCMPFieldBridge[1];
853 }
854 else
855 {
856 tableFields = new JDBCCMPFieldBridge[tableFields.length + 1];
857 System.arraycopy(tmpFields, 0, tableFields, 0, tmpFields.length);
858 }
859 int index = tableFields.length - 1;
860 tableFields[index] = field;
861
862 return index;
863 }
864
865 public JDBCFieldBridge[] getTableFields()
866 {
867 return tableFields;
868 }
869
870 /**
871 * Marks the context as removed.
872 * @param ctx instance's context
873 */
874 public void setRemoved(EntityEnterpriseContext ctx)
875 {
876 getEntityState(ctx).setRemoved();
877 }
878
879 /**
880 * @param ctx instance's context.
881 * @return true if instance was removed.
882 */
883 public boolean isRemoved(EntityEnterpriseContext ctx)
884 {
885 return getEntityState(ctx).isRemoved();
886 }
887
888 /**
889 * Marks an instance as being removed
890 */
891 public void setIsBeingRemoved(EntityEnterpriseContext ctx)
892 {
893 getEntityState(ctx).setIsBeingRemoved();
894 }
895
896 /**
897 * @param ctx instance's context.
898 * @return true if instance is being removed.
899 */
900 public boolean isBeingRemoved(EntityEnterpriseContext ctx)
901 {
902 return getEntityState(ctx).isBeingRemoved();
903 }
904
905 /**
906 * Marks the instance as scheduled for cascade delete (not for batch cascade delete)
907 * @param ctx instance's context.
908 */
909 public void scheduleForCascadeDelete(EntityEnterpriseContext ctx)
910 {
911 getEntityState(ctx).scheduleForCascadeDelete();
912 if(log.isTraceEnabled())
913 log.trace("Scheduled for cascade-delete: " + ctx.getId());
914 }
915
916 /**
917 * @param ctx instance's context.
918 * @return true if instance was scheduled for cascade delete (not for batch cascade delete)
919 */
920 public boolean isScheduledForCascadeDelete(EntityEnterpriseContext ctx)
921 {
922 return getEntityState(ctx).isScheduledForCascadeDelete();
923 }
924
925 /**
926 * Marks the instance as scheduled for batch cascade delete (not for cascade delete)
927 * @param ctx instance's context.
928 */
929 public void scheduleForBatchCascadeDelete(EntityEnterpriseContext ctx)
930 {
931 getEntityState(ctx).scheduleForBatchCascadeDelete();
932 if(log.isTraceEnabled())
933 log.trace("Scheduled for batch-cascade-delete: " + ctx.getId());
934 }
935
936 /**
937 * @param ctx instance's context.
938 * @return true if instance was scheduled for batch cascade delete (not for cascade delete)
939 */
940 public boolean isScheduledForBatchCascadeDelete(EntityEnterpriseContext ctx)
941 {
942 return getEntityState(ctx).isScheduledForBatchCascadeDelete();
943 }
944
945 private static EntityState getEntityState(EntityEnterpriseContext ctx)
946 {
947 JDBCContext jdbcCtx = (JDBCContext)ctx.getPersistenceContext();
948 EntityState entityState = jdbcCtx.getEntityState();
949 if(entityState == null)
950 throw new IllegalStateException("Entity state is null.");
951 return entityState;
952 }
953
954 private void loadCMPFields(JDBCEntityMetaData metadata)
955 throws DeploymentException
956 {
957 // only non pk fields are stored here at first and then later
958 // the pk fields are added to the front (makes sql easier to read)
959 List cmpFieldsMD = metadata.getCMPFields();
960 List cmpFieldsList = new ArrayList(cmpFieldsMD.size());
961 // primary key cmp fields
962 List pkFieldsList = new ArrayList(cmpFieldsMD.size());
963
964 // create pk fields
965 for(int i = 0; i < cmpFieldsMD.size(); ++i)
966 {
967 JDBCCMPFieldMetaData fieldMD = (JDBCCMPFieldMetaData)cmpFieldsMD.get(i);
968 if(fieldMD.isPrimaryKeyMember())
969 {
970 JDBCCMPFieldBridge cmpField = createCMPField(metadata, fieldMD);
971 pkFieldsList.add(cmpField);
972 }
973 }
974
975 // create non-pk cmp fields
976 for(int i = 0; i < cmpFieldsMD.size(); ++i)
977 {
978 JDBCCMPFieldMetaData fieldMD = (JDBCCMPFieldMetaData)cmpFieldsMD.get(i);
979 if(!fieldMD.isPrimaryKeyMember())
980 {
981 JDBCCMPFieldBridge cmpField = createCMPField(metadata, fieldMD);
982 cmpFieldsList.add(cmpField);
983 }
984 }
985
986 // save the pk fields in the pk field array
987 primaryKeyFields = new JDBCCMPFieldBridge[pkFieldsList.size()];
988 for(int i = 0; i < pkFieldsList.size(); ++i)
989 primaryKeyFields[i] = (JDBCCMPFieldBridge)pkFieldsList.get(i);
990
991 // add the pk fields to the front of the cmp list, per guarantee above
992 cmpFields = new JDBCCMPFieldBridge[cmpFieldsMD.size() - primaryKeyFields.length];
993 int cmpFieldIndex = 0;
994 for(int i = 0; i < cmpFieldsList.size(); ++i)
995 cmpFields[cmpFieldIndex++] = (JDBCCMPFieldBridge)cmpFieldsList.get(i);
996 }
997
998 private void loadCMRFields(JDBCEntityMetaData metadata)
999 throws DeploymentException
1000 {
1001 cmrFields = new JDBCCMRFieldBridge[metadata.getRelationshipRoles().size()];
1002 // create each field
1003 int cmrFieldIndex = 0;
1004 for(Iterator iter = metadata.getRelationshipRoles().iterator(); iter.hasNext();)
1005 {
1006 JDBCRelationshipRoleMetaData relationshipRole = (JDBCRelationshipRoleMetaData)iter.next();
1007 JDBCCMRFieldBridge cmrField = new JDBCCMRFieldBridge(this, manager, relationshipRole);
1008 cmrFields[cmrFieldIndex++] = cmrField;
1009 }
1010 }
1011
1012 private void loadLoadGroups(JDBCEntityMetaData metadata)
1013 throws DeploymentException
1014 {
1015 loadGroupMasks = new HashMap();
1016
1017 // load optimistic locking mask and add it to all the load group masks
1018 JDBCOptimisticLockingMetaData olMD = metadata.getOptimisticLocking();
1019 if(olMD != null)
1020 {
1021 if(versionField != null)
1022 {
1023 defaultLockGroupMask = new boolean[tableFields.length];
1024 defaultLockGroupMask[versionField.getTableIndex()] = true;
1025 versionField.setLockingStrategy(LockingStrategy.VERSION);
1026 }
1027 else if(olMD.getGroupName() != null)
1028 {
1029 defaultLockGroupMask = loadGroupMask(olMD.getGroupName(), null);
1030 for(int i = 0; i < tableFields.length; ++i)
1031 {
1032 if(defaultLockGroupMask[i])
1033 {
1034 JDBCCMPFieldBridge tableField = tableFields[i];
1035 tableField.setLockingStrategy(LockingStrategy.GROUP);
1036 tableField.addDefaultFlag(ADD_TO_WHERE_ON_UPDATE);
1037 }
1038 }
1039 }
1040 else // read or modified strategy
1041 {
1042 LockingStrategy strategy =
1043 (olMD.getLockingStrategy() == JDBCOptimisticLockingMetaData.READ_STRATEGY ?
1044 LockingStrategy.READ : LockingStrategy.MODIFIED
1045 );
1046 for(int i = 0; i < tableFields.length; ++i)
1047 {
1048 JDBCCMPFieldBridge field = tableFields[i];
1049 if(!field.isPrimaryKeyMember())
1050 field.setLockingStrategy(strategy);
1051 }
1052 }
1053 }
1054
1055 // add the * load group
1056 boolean[] defaultLoadGroup = new boolean[tableFields.length];
1057 Arrays.fill(defaultLoadGroup, true);
1058 for(int i = 0; i < primaryKeyFields.length; ++i)
1059 {
1060 int tableIndex = primaryKeyFields[i].getTableIndex();
1061 defaultLoadGroup[tableIndex] = false;
1062 }
1063 loadGroupMasks.put(DEFAULT_LOADGROUP_NAME, defaultLoadGroup);
1064
1065 // put each group in the load groups map by group name
1066 Iterator groupNames = metadata.getLoadGroups().keySet().iterator();
1067 while(groupNames.hasNext())
1068 {
1069 // get the group name
1070 String groupName = (String)groupNames.next();
1071 boolean[] loadGroup = loadGroupMask(groupName, defaultLockGroupMask);
1072 loadGroupMasks.put(groupName, loadGroup);
1073 }
1074 loadGroupMasks = Collections.unmodifiableMap(loadGroupMasks);
1075 }
1076
1077 private boolean[] loadGroupMask(String groupName, boolean[] defaultGroup)
1078 throws DeploymentException
1079 {
1080 List fieldNames = metadata.getLoadGroup(groupName);
1081 boolean[] group = new boolean[tableFields.length];
1082 if(defaultGroup != null)
1083 System.arraycopy(defaultGroup, 0, group, 0, group.length);
1084 for(Iterator iter = fieldNames.iterator(); iter.hasNext();)
1085 {
1086 String fieldName = (String)iter.next();
1087 JDBCFieldBridge field = (JDBCFieldBridge)getFieldByName(fieldName);
1088 if(field == null)
1089 throw new DeploymentException(
1090 "Field " + fieldName + " not found for entity " + getEntityName());
1091
1092 if(field instanceof JDBCCMRFieldBridge)
1093 {
1094 JDBCCMRFieldBridge cmrField = (JDBCCMRFieldBridge)field;
1095 if(cmrField.hasForeignKey())
1096 {
1097 JDBCCMPFieldBridge[] fkFields = (JDBCCMPFieldBridge[]) cmrField.getForeignKeyFields();
1098 for(int i = 0; i < fkFields.length; ++i)
1099 {
1100 group[fkFields[i].getTableIndex()] = true;
1101 }
1102 }
1103 else
1104 {
1105 throw new DeploymentException("Only CMR fields that have " +
1106 "a foreign-key may be a member of a load group: " +
1107 "fieldName=" + fieldName);
1108 }
1109 }
1110 else
1111 {
1112 group[((JDBCCMPFieldBridge)field).getTableIndex()] = true;
1113 }
1114 }
1115 return group;
1116 }
1117
1118 private void loadEagerLoadGroup(JDBCEntityMetaData metadata)
1119 {
1120 String eagerLoadGroupName = metadata.getEagerLoadGroup();
1121 if(eagerLoadGroupName == null)
1122 {
1123 // can be null in case of <eager-load-group/>, meaning empty load group
1124 eagerLoadGroupMask = defaultLockGroupMask;
1125 }
1126 else
1127 eagerLoadGroupMask = (boolean[])loadGroupMasks.get(eagerLoadGroupName);
1128 }
1129
1130 private void loadLazyLoadGroups(JDBCEntityMetaData metadata)
1131 {
1132 List lazyGroupNames = metadata.getLazyLoadGroups();
1133 lazyLoadGroupMasks = new ArrayList(lazyGroupNames.size());
1134 for(Iterator lazyLoadGroupNames = lazyGroupNames.iterator(); lazyLoadGroupNames.hasNext();)
1135 {
1136 String lazyLoadGroupName = (String)lazyLoadGroupNames.next();
1137 lazyLoadGroupMasks.add(loadGroupMasks.get(lazyLoadGroupName));
1138 }
1139 lazyLoadGroupMasks = Collections.unmodifiableList(lazyLoadGroupMasks);
1140 }
1141
1142 private JDBCCMPFieldBridge createCMPField(JDBCEntityMetaData metadata,
1143 JDBCCMPFieldMetaData cmpFieldMetaData)
1144 throws DeploymentException
1145 {
1146 JDBCCMPFieldBridge cmpField;
1147 if(metadata.isCMP1x())
1148 cmpField = new JDBCCMP1xFieldBridge(manager, cmpFieldMetaData);
1149 else
1150 cmpField = new JDBCCMP2xFieldBridge(manager, cmpFieldMetaData);
1151 return cmpField;
1152 }
1153
1154 private void loadSelectors(JDBCEntityMetaData metadata)
1155 {
1156 // Don't know if this is the best way to do this. Another way would be
1157 // to deligate seletors to the JDBCFindEntitiesCommand, but this is
1158 // easier now.
1159 selectorsByMethod = new HashMap(metadata.getQueries().size());
1160 Iterator definedFinders = manager.getMetaData().getQueries().iterator();
1161 while(definedFinders.hasNext())
1162 {
1163 JDBCQueryMetaData q = (JDBCQueryMetaData)definedFinders.next();
1164 if(q.getMethod().getName().startsWith("ejbSelect"))
1165 selectorsByMethod.put(q.getMethod(), new JDBCSelectorBridge(manager, q));
1166 }
1167 selectorsByMethod = Collections.unmodifiableMap(selectorsByMethod);
1168 }
1169
1170 private void addCMPField(JDBCCMPFieldBridge field)
1171 {
1172 JDBCCMPFieldBridge[] tmpCMPFields = cmpFields;
1173 cmpFields = new JDBCCMPFieldBridge[cmpFields.length + 1];
1174 System.arraycopy(tmpCMPFields, 0, cmpFields, 0, tmpCMPFields.length);
1175 cmpFields[tmpCMPFields.length] = field;
1176 }
1177
1178 public class EntityState
1179 {
1180 private static final byte REMOVED = 1;
1181 private static final byte SCHEDULED_FOR_CASCADE_DELETE = 2;
1182 private static final byte SCHEDULED_FOR_BATCH_CASCADE_DELETE = 4;
1183 private static final byte IS_BEING_REMOVED = 8;
1184
1185 /** indicates whether ejbCreate method was executed */
1186 private boolean ejbCreateDone = false;
1187 /** indicates whether ejbPostCreate method was executed */
1188 private boolean ejbPostCreateDone = false;
1189
1190 private byte entityFlags;
1191
1192 /** array of field flags*/
1193 private final byte[] fieldFlags = new byte[tableFields.length];
1194
1195 public EntityState()
1196 {
1197 for(int i = 0; i < tableFields.length; ++i)
1198 {
1199 fieldFlags[i] = tableFields[i].getDefaultFlags();
1200 }
1201 }
1202
1203 public void setRemoved()
1204 {
1205 entityFlags |= REMOVED;
1206 entityFlags &= ~(SCHEDULED_FOR_CASCADE_DELETE | SCHEDULED_FOR_BATCH_CASCADE_DELETE | IS_BEING_REMOVED);
1207 }
1208
1209 public boolean isRemoved()
1210 {
1211 return (entityFlags & REMOVED) > 0;
1212 }
1213
1214 public void setIsBeingRemoved()
1215 {
1216 entityFlags |= IS_BEING_REMOVED;
1217 }
1218
1219 public boolean isBeingRemoved()
1220 {
1221 return (entityFlags & IS_BEING_REMOVED) > 0;
1222 }
1223
1224 public void scheduleForCascadeDelete()
1225 {
1226 entityFlags |= SCHEDULED_FOR_CASCADE_DELETE;
1227 }
1228
1229 public boolean isScheduledForCascadeDelete()
1230 {
1231 return (entityFlags & SCHEDULED_FOR_CASCADE_DELETE) > 0;
1232 }
1233
1234 public void scheduleForBatchCascadeDelete()
1235 {
1236 entityFlags |= SCHEDULED_FOR_BATCH_CASCADE_DELETE | SCHEDULED_FOR_CASCADE_DELETE;
1237 }
1238
1239 public boolean isScheduledForBatchCascadeDelete()
1240 {
1241 return (entityFlags & SCHEDULED_FOR_BATCH_CASCADE_DELETE) > 0;
1242 }
1243
1244 public void setCreated()
1245 {
1246 ejbCreateDone = true;
1247 ejbPostCreateDone = true;
1248 }
1249
1250 public boolean isCreated()
1251 {
1252 return ejbCreateDone && ejbPostCreateDone;
1253 }
1254
1255 /**
1256 * @param fieldIndex index of the field
1257 * @return true if the field is loaded
1258 */
1259 public boolean isLoaded(int fieldIndex)
1260 {
1261 return (fieldFlags[fieldIndex] & LOADED) > 0;
1262 }
1263
1264 /**
1265 * Marks the field as loaded.
1266 * @param fieldIndex index of the field.
1267 */
1268 public void setLoaded(int fieldIndex)
1269 {
1270 fieldFlags[fieldIndex] |= LOADED;
1271 }
1272
1273 /**
1274 * Marks the field to be loaded.
1275 * @param fieldIndex index of the field.
1276 */
1277 public void setLoadRequired(int fieldIndex)
1278 {
1279 fieldFlags[fieldIndex] |= LOAD_REQUIRED;
1280 }
1281
1282 /**
1283 * Marks the field to be updated.
1284 * @param fieldIndex index of the field.
1285 */
1286 public void setUpdateRequired(int fieldIndex)
1287 {
1288 fieldFlags[fieldIndex] |= DIRTY;
1289 }
1290
1291 /**
1292 * The field will be checked for dirty state at commit.
1293 * @param fieldIndex index of the field.
1294 */
1295 public void setCheckDirty(int fieldIndex)
1296 {
1297 fieldFlags[fieldIndex] |= CHECK_DIRTY;
1298 }
1299
1300 /**
1301 * @param fieldIndex the index of the field that should be checked for dirty state.
1302 * @return true if the field should be checked for dirty state.
1303 */
1304 public boolean isCheckDirty(int fieldIndex)
1305 {
1306 return (fieldFlags[fieldIndex] & CHECK_DIRTY) > 0;
1307 }
1308
1309 /**
1310 * Marks the field as clean.
1311 * @param fieldIndex nextIndex of the field.
1312 */
1313 public void setClean(int fieldIndex)
1314 {
1315 fieldFlags[fieldIndex] &= ~(CHECK_DIRTY | DIRTY | LOCKED);
1316 }
1317
1318 /**
1319 * Resets field flags.
1320 * @param fieldIndex nextIndex of the field.
1321 */
1322 public void resetFlags(int fieldIndex)
1323 {
1324 fieldFlags[fieldIndex] = tableFields[fieldIndex].getDefaultFlags();
1325 }
1326
1327 public FieldIterator getDirtyIterator(EntityEnterpriseContext ctx)
1328 {
1329 return new MaskFieldIterator((byte)(DIRTY | ADD_TO_SET_ON_UPDATE));
1330 }
1331
1332 public boolean hasLockedFields()
1333 {
1334 boolean result = false;
1335 for(int i = 0; i < fieldFlags.length; ++i)
1336 {
1337 if((fieldFlags[i] & (LOCKED | ADD_TO_WHERE_ON_UPDATE)) > 0)
1338 {
1339 result = true;
1340 break;
1341 }
1342 }
1343 return result;
1344 }
1345
1346 public FieldIterator getLockedIterator(EntityEnterpriseContext ctx)
1347 {
1348 return new MaskFieldIterator((byte)(LOCKED | ADD_TO_WHERE_ON_UPDATE));
1349 }
1350
1351 public boolean lockValue(int fieldIndex)
1352 {
1353 boolean lock = false;
1354 byte fieldFlag = fieldFlags[fieldIndex];
1355 if((fieldFlag & LOADED) > 0 && (fieldFlag & LOCKED) == 0)
1356 {
1357 fieldFlags[fieldIndex] |= LOCKED;
1358 lock = true;
1359 }
1360 return lock;
1361 }
1362
1363 public FieldIterator getLoadIterator(EntityEnterpriseContext ctx)
1364 {
1365 return new MaskFieldIterator(LOAD_REQUIRED);
1366 }
1367
1368 // Inner
1369
1370 private class MaskFieldIterator implements FieldIterator
1371 {
1372 private final byte flagMask;
1373 private int nextIndex = 0;
1374 private int curIndex = -1;
1375
1376 public MaskFieldIterator(byte flagMask)
1377 {
1378 this.flagMask = flagMask;
1379 }
1380
1381 public boolean hasNext()
1382 {
1383 while(nextIndex < fieldFlags.length)
1384 {
1385 if((fieldFlags[nextIndex] & flagMask) > 0)
1386 {
1387 return true;
1388 }
1389
1390 ++nextIndex;
1391 }
1392
1393 return false;
1394 }
1395
1396 public JDBCCMPFieldBridge next()
1397 {
1398 if(!hasNext())
1399 throw new NoSuchElementException();
1400 curIndex = nextIndex;
1401 return tableFields[nextIndex++];
1402 }
1403
1404 public void remove()
1405 {
1406 fieldFlags[curIndex] &= ~flagMask;
1407 }
1408
1409 public void removeAll()
1410 {
1411 int inversedMask = ~flagMask;
1412 for(int i = 0; i < fieldFlags.length; ++i)
1413 fieldFlags[i] &= inversedMask;
1414 }
1415
1416 public void reset()
1417 {
1418 nextIndex = 0;
1419 curIndex = -1;
1420 }
1421 }
1422 }
1423
1424 public static final FieldIterator EMPTY_FIELD_ITERATOR = new FieldIterator()
1425 {
1426 public boolean hasNext()
1427 {
1428 return false;
1429 }
1430
1431 public JDBCCMPFieldBridge next()
1432 {
1433 throw new NoSuchElementException();
1434 }
1435
1436 public void remove()
1437 {
1438 throw new UnsupportedOperationException();
1439 }
1440
1441 public void removeAll()
1442 {
1443 throw new UnsupportedOperationException();
1444 }
1445
1446 public void reset()
1447 {
1448 }
1449 };
1450
1451 public static interface FieldIterator
1452 {
1453 /**
1454 * @return true if there are more fields to iterate through.
1455 */
1456 boolean hasNext();
1457
1458 /**
1459 * @return the next field.
1460 */
1461 JDBCCMPFieldBridge next();
1462
1463 /**
1464 * Removes the current field from the iterator (not from the underlying array or another source)
1465 */
1466 void remove();
1467
1468 /**
1469 * Removes all the fields from the iterator (not from the underlying array or another source).
1470 */
1471 void removeAll();
1472
1473 /**
1474 * Resets the current position to the first field.
1475 */
1476 void reset();
1477 }
1478 }