/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.enterprise.deployment;
import java.util.*;
import java.lang.reflect.*;
import javax.ejb.EJBException;
import com.sun.enterprise.util.LocalStringManagerImpl;
import com.sun.enterprise.util.TypeUtil;
import java.util.logging.Level;
import com.sun.enterprise.deployment.util.DOLUtils;
/**
* This class contains information about the persistent state
* (abstract persistence schema)
* for EJB2.0 CMP EntityBeans and Join Objects.
*
* @author Sanjeev Krishnan
*/
public final class PersistenceDescriptor extends Descriptor {
private Set cmpFields = new HashSet();
// there can be 0 or more pkey fields
// This set contains FieldDescriptors for fields from bean or pkey class
private Set pkeyFields = new HashSet();
// true if primkey-field is set or for container-generated pk field
private boolean pkeyIsOneField = false;
// false for beans with no primkey-field and pk class = Object
private boolean pkeyFieldSpecified = true;
private String primaryKeyClassName;
private boolean pkeyStuffInitialized = false;
// true for beans whose primary fields are all primitive fields
private boolean pkeyFieldsAllPrimitive = false;
private static LocalStringManagerImpl localStrings =
new LocalStringManagerImpl(PersistenceDescriptor.class);
private EjbCMPEntityDescriptor parentDesc; //the bean whose persistence I describe
private transient Class persistentClass; // the bean class
private transient Class stateClass; // the class which holds persistent fields
private transient Class primaryKeyClass;
private PersistentFieldInfo[] persFieldInfo;
private PersistentFieldInfo[] persNoPkeyFieldInfo;
private PersistentFieldInfo[] pkeyFieldInfo;
private boolean fieldInfoInitialized=false;
private PersistentFieldInfo[] fkeyFields;
private CMRFieldInfo[] cmrFieldInfo;
private transient Field[] pkeyClassPkeyFields; // fields in primary key class
private Hashtable queries = new Hashtable();
private HashSet allQueriedMethods;
public PersistenceDescriptor() {
}
/**
* The copy constructor.
*/
public PersistenceDescriptor(PersistenceDescriptor pers) {
super(pers);
this.getCMPFields().addAll(pers.getCMPFields());
//this.primaryKeyFieldDesc = pers.primaryKeyFieldDesc;
}
public String getCMRFieldReturnType(String field) {
String returnType = "java.util.Collection";
try {
if( !field.trim().equals("") ) {
Class persClass = getPersistentClass();
String methodName = "get" + field.substring(0,1).toUpperCase()
+ field.substring(1);
Method method = TypeUtil.getMethod
(persClass, persClass.getClassLoader(), methodName,
new String[] {});
returnType = method.getReturnType().getName();
}
} catch(Throwable t) {
if(DOLUtils.getDefaultLogger().isLoggable(Level.FINE)) {
DOLUtils.getDefaultLogger().log(Level.FINE, t.toString(), t);
}
}
return returnType;
}
/**
* Called from EjbCMPEntityDescriptor
* when some classes in this object are updated.
*/
public boolean classesChanged() {
// XXX Remove any fields marked as persistent that no longer exist
// in bean class as getter/setter.
persistentClass = null;
stateClass = null;
Class persClass = getPersistentClass();
Vector fieldDescriptors = parentDesc.getFieldDescriptors();
// Remove obsolete cmp fields
if( this.cmpFields != null ) {
for(Iterator iter = cmpFields.iterator(); iter.hasNext();) {
FieldDescriptor next = (FieldDescriptor) iter.next();
if( !fieldDescriptors.contains(next) ) {
iter.remove();
}
}
}
// Remove obsolete pkey fields
if( this.pkeyFields != null ) {
for(Iterator iter = pkeyFields.iterator(); iter.hasNext();) {
FieldDescriptor next = (FieldDescriptor) iter.next();
if( !fieldDescriptors.contains(next) ) {
iter.remove();
}
}
}
FieldDescriptor primKeyFieldDesc = parentDesc.getPrimaryKeyFieldDesc();
if( (primKeyFieldDesc != null) &&
!fieldDescriptors.contains(primKeyFieldDesc) ) {
parentDesc.setPrimaryKeyFieldDesc(null);
}
// CMP 2.x
// Remove queries for methods no longer in allQueriedMethods
// First clone old set of queries
Hashtable queriesClone = (Hashtable) queries.clone();
queries = new Hashtable();
initializeAllQueriedMethods();
Iterator it = queriesClone.keySet().iterator();
while ( it.hasNext() ) {
Method oldMethod = (Method)it.next();
Method newMethod = findEquivalentMethod(allQueriedMethods,
oldMethod);
if( newMethod != null ) {
QueryDescriptor oldQuery = (QueryDescriptor)
queriesClone.get(oldMethod);
QueryDescriptor newQuery =
new QueryDescriptor(oldQuery, newMethod);
// Only add to list of methods having queries if
// it's still in the class.
queries.put(newMethod, newQuery);
}
}
// Force the persistence descriptor to regenerate its
// derived info.
invalidate();
return false;
}
/**
* Search for a matching method in a list of methods
* that might have been loaded with a different classloader.
* Because of this possibility, we can't use
* java.lang.reflect.Method.equals().
* @return matched method or NULL
*/
private Method findEquivalentMethod(Collection methods,
Method methodToMatch) {
Method matchedMethod = null;
for(Iterator iter = methods.iterator(); iter.hasNext();) {
Object o = iter.next();
Method next;
if (o instanceof Method) {
next = (Method) o;
} else {
next = ((MethodDescriptor) o).getMethod(parentDesc);
if (next==null) {
return null;
}
}
// Compare methods, ignoring declaring class.
if( methodsEqual(next, methodToMatch, false) ) {
matchedMethod = next;
break;
}
}
return matchedMethod;
}
/**
* Checks whether two methods that might have been loaded by
* different class loaders are equal.
* @param compareDeclaringClass if true, declaring class will
* be considered as part of equality test.
*/
private boolean methodsEqual(Method m1, Method m2,
boolean compareDeclaringClass) {
boolean equal = false;
do {
String m1Name = m1.getName();
String m2Name = m2.getName();
if( !m1Name.equals(m2Name) ) { break; }
String m1DeclaringClass = m1.getDeclaringClass().getName();
String m2DeclaringClass = m2.getDeclaringClass().getName();
if( compareDeclaringClass ) {
if( !m1DeclaringClass.equals(m2DeclaringClass) ) { break; }
}
Class[] m1ParamTypes = m1.getParameterTypes();
Class[] m2ParamTypes = m2.getParameterTypes();
if( m1ParamTypes.length != m2ParamTypes.length ) { break; }
equal = true;
for(int pIndex = 0; pIndex < m1ParamTypes.length; pIndex++) {
String m1ParamClass = m1ParamTypes[pIndex].getName();
String m2ParamClass = m2ParamTypes[pIndex].getName();
if( !m1ParamClass.equals(m2ParamClass) ) {
equal = false;
break;
}
}
} while(false);
return equal;
}
/**
* Checks whether two methods that might have been loaded by
* different class loaders are equal.
* @param compareDeclaringClass if true, declaring class will
* be considered as part of equality test.
*/
private boolean methodsEqual(MethodDescriptor m1, Method m2,
boolean compareDeclaringClass) {
Method m = m1.getMethod(parentDesc);
return methodsEqual(m, m2, compareDeclaringClass);
}
public void setParentDescriptor(Descriptor parentDesc)
{
this.parentDesc = (EjbCMPEntityDescriptor)parentDesc;
}
public Descriptor getParentDescriptor()
{
return parentDesc;
}
public EjbBundleDescriptor getEjbBundleDescriptor()
{
return parentDesc.getEjbBundleDescriptor();
}
/**
* Get all CMR fields of this bean. All relationships
* are stored in EjbBundleDescriptor to avoid the complexity of
* keeping the two sets consistent. NOTE : To add or remove
* a relationship use EjbBundleDescriptor.
*/
public Set getRelationships()
{
Set allRelationships = getEjbBundleDescriptor().getRelationships();
Set myRelationships = new HashSet();
for(Iterator iter = allRelationships.iterator(); iter.hasNext();) {
RelationshipDescriptor next = (RelationshipDescriptor) iter.next();
if( next.hasParticipant(parentDesc) ) {
myRelationships.add(next);
}
}
return myRelationships;
}
/**
* Return array of CMRFieldInfo objects for all CMR fields.
*/
public CMRFieldInfo[] getCMRFieldInfo()
{
if ( cmrFieldInfo == null ) {
try {
initCMRFieldStuff();
} catch ( Exception ex ) {
DOLUtils.getDefaultLogger().log(Level.SEVERE, "enterprise.deployment.backend.invalidDescriptorMappingFailure",
new Object[] {ex.toString()});
throw new EJBException(ex);
}
}
return cmrFieldInfo;
}
/**
* Return the CMRFieldInfo object for the given CMR field
*/
public CMRFieldInfo getCMRFieldInfoByName(String fieldName)
{
CMRFieldInfo[] cmrf = this.getCMRFieldInfo();
for ( int i=0; i<cmrf.length; i++ ) {
if ( cmrf[i].name.equals(fieldName) )
return cmrf[i];
}
throw new EJBException("CMRFieldInfo not found for field "+fieldName);
}
/**
* Ensures that persistence descriptor will regenerate its
* derived information after changes have been made to
* persistent characteristics.
*/
public void invalidate() {
cmrFieldInfo = null;
persFieldInfo = null;
fieldInfoInitialized = false;
pkeyStuffInitialized = false;
}
private void initCMRFieldStuff()
throws Exception
{
if ( !fieldInfoInitialized )
initializeFieldInfo();
Set relationships = getRelationships();
Iterator it = relationships.iterator();
// set to the biggest possible size when all relationships
// are self-referencing
CMRFieldInfo[] cmrFieldInfo2 =
new CMRFieldInfo[relationships.size() * 2];
int count = 0;
while ( it.hasNext() ) {
RelationshipDescriptor rd = (RelationshipDescriptor)it.next();
RelationRoleDescriptor source = rd.getSource();
RelationRoleDescriptor sink = rd.getSink();
RelationRoleDescriptor myroles[];
// if this is a self-referencing relationship, initialize
// both source and sink cmr fields
if ( source.getPersistenceDescriptor() ==
sink.getPersistenceDescriptor() ) {
myroles = new RelationRoleDescriptor[2];
myroles[0] = source;
myroles[1] = sink;
} else {
myroles = new RelationRoleDescriptor[1];
if ( source.getPersistenceDescriptor() == this ) {
myroles[0] = source;
} else {
myroles[0] = sink;
}
}
// for all relation role elements in myroles, initialize
// their cmr fields and put them in cmrFieldInfo2
for (int ii = 0; ii < myroles.length; ii++) {
CMRFieldInfo finfo = new CMRFieldInfo();
cmrFieldInfo2[count++] = finfo;
PersistenceDescriptor partnerPers =
myroles[ii].getPartner().getPersistenceDescriptor();
// Check if partner has a local interface
EjbCMPEntityDescriptor partner =
myroles[ii].getPartner().getOwner();
if ( !partner.isLocalInterfacesSupported() &&
myroles[ii].getCMRField() != null ) {
throw new RuntimeException(
"No local interface for target bean of CMR field");
}
String type;
if ( myroles[ii].getPartner().getIsMany() == false ) {
// 1-1 and many-1
if ( partner.isLocalInterfacesSupported() )
type = partner.getLocalClassName();
else
// a unidirectional relation, partner is
// remote-only bean
type = partner.getPrimaryKeyClassName();
}
else {
// 1-many and many-many
type = myroles[ii].getCMRFieldType();
if ( type == null ) {
// A unidirectional relationship from partner
// to this obj
type = "java.util.Collection";
}
}
finfo.type = getClass(type);
finfo.name = myroles[ii].getCMRField();
if ( finfo.name == null ) {
// A unidirectional relationship from partner to this obj.
// We need to maintain a pointer to partner anyway.
finfo.name = myroles[ii].composeReverseCmrFieldName();
}
finfo.role = myroles[ii];
myroles[ii].setCMRFieldInfo(finfo);
if ( rd.isOneOne() && fkeyFields != null ) {
// set foreign key fields corresponding to this CMR field
PersistentFieldInfo[] cmrFkeyFields;
PersistentFieldInfo[] partnerPkeyFields =
partnerPers.getPkeyFieldInfo();
cmrFkeyFields =
new PersistentFieldInfo[partnerPkeyFields.length];
for ( int i=0; i<partnerPkeyFields.length; i++ ) {
String fkeyName = "_" + finfo.name + "_"
+ partnerPkeyFields[i].name;
for ( int j=0; j<fkeyFields.length; j++ ) {
if ( fkeyFields[j].name.equals(fkeyName) )
cmrFkeyFields[i] = fkeyFields[j];
}
}
finfo.fkeyFields = cmrFkeyFields;
}
}
}
// initialize the cmrFieldInfo array with the actual size
// and copy the non-null values of cmrFieldInfo2 to it
cmrFieldInfo = new CMRFieldInfo[count];
System.arraycopy(cmrFieldInfo2, 0, cmrFieldInfo, 0, count);
// Sort cmrFieldInfo in alphabetical order of CMR field name
for ( int i=cmrFieldInfo.length-1; i>0; i-- ) {
for ( int j=0; j<i; j++ ) {
if ( cmrFieldInfo[j].name.compareTo(cmrFieldInfo[j+1].name)
> 0 ) {
CMRFieldInfo tmp = cmrFieldInfo[j];
cmrFieldInfo[j] = cmrFieldInfo[j+1];
cmrFieldInfo[j+1] = tmp;
}
}
}
}
public void clearCMPFields() {
this.cmpFields.clear();
setCMPFields(this.cmpFields);
}
public void addCMPField(String field) {
addCMPField(new FieldDescriptor(field));
}
public void addCMPField(FieldDescriptor fieldDesc) {
this.cmpFields.add(fieldDesc);
setCMPFields(this.cmpFields);
}
public void removeCMPField(String field) {
removeCMPField(new FieldDescriptor(field));
}
public void removeCMPField(FieldDescriptor fieldDesc) {
this.cmpFields.remove(fieldDesc);
setCMPFields(this.cmpFields);
}
/**
* Set the FieldDescriptor objects that the EJB container will persist
* for this bean.
*/
public void setCMPFields(Set cmpFields) {
this.cmpFields = cmpFields;
persFieldInfo = null;
fieldInfoInitialized = false;
super.changed();
}
/**
* Has the supplied field object been deemed persistent.
*/
public boolean isCMPField(String field) {
return this.getCMPFields().contains(new FieldDescriptor(field));
}
/**
* Return the Set of fields deemed persistent.
* The elements of this Set are FieldDescriptor objects.
* This Set should be modified by calling addCMPField, removeCMPField
*/
public Set getCMPFields() {
return this.cmpFields;
}
/**
public void clearPkeyFields() {
this.pkeyFields.clear();
setPkeyFields(this.pkeyFields);
}
public void addPkeyField(String field) {
addPkeyField(new FieldDescriptor(field));
}
public void addPkeyField(FieldDescriptor fieldDesc) {
this.pkeyFields.add(fieldDesc);
setPkeyFields(this.pkeyFields);
}
public void removePkeyField(String field) {
removePkeyField(new FieldDescriptor(field));
}
public void removePkeyField(FieldDescriptor fieldDesc) {
this.pkeyFields.remove(fieldDesc);
setPkeyFields(this.pkeyFields);
}
**/
/**
* Set the FieldDescriptor objects for primary key fields
* for this bean.
*/
public void setPkeyFields(Set pkeyFields) {
this.pkeyFields = pkeyFields;
fieldInfoInitialized = false;
persFieldInfo = null;
pkeyStuffInitialized = false;
super.changed();
}
/**
* Return the Set of primary key fields.
* The elements of this Set are FieldDescriptor objects.
* This Set can be modified by calling addPkeyField, removePkeyField
*/
public Set getPkeyFields() {
if ( !pkeyStuffInitialized )
initPkeyInfo();
return pkeyFields;
}
/**
* Is the supplied field object one of the pkey fields.
*/
public boolean isPkeyField(String field) {
return isPkeyField(new FieldDescriptor(field));
}
public boolean isPkeyField(FieldDescriptor fieldDesc) {
return this.getPkeyFields().contains(fieldDesc);
}
/**
* Initialize pkeyFields, pkeyIsOneField, primaryKeyClassName
* Must be called after this PersistenceDescriptor has been attached
* to the Ejb/JoinDescriptor which has been
* attached to the EjbBundleDescriptor.
*/
private void initPkeyInfo()
{
try {
pkeyIsOneField = false;
pkeyFieldSpecified = true;
primaryKeyClassName = parentDesc.getPrimaryKeyClassName();
FieldDescriptor fd = parentDesc.getPrimaryKeyFieldDesc();
if ( pkeyFields == null || pkeyFields.size() == 0 ) {
pkeyFields = new HashSet();
if ( fd != null ) // primkey-field was set
pkeyFields.add(fd);
else if (!primaryKeyClassName.equals("java.lang.Object")) {
// get fields of primaryKeyClass
primaryKeyClass = getClass(primaryKeyClassName);
Field[] fields = primaryKeyClass.getFields();
pkeyFieldsAllPrimitive = true;
for ( int i=0; i<fields.length; i++ ) {
// ignore static or final fields
int m = fields[i].getModifiers();
if ( Modifier.isStatic(m) || Modifier.isFinal(m) )
continue;
if ( !fields[i].getType().isPrimitive() )
pkeyFieldsAllPrimitive = false;
pkeyFields.add(new FieldDescriptor(
fields[i].getName()));
}
}
else {
// Will use PM-generated primary key
primaryKeyClass = getClass(primaryKeyClassName);
pkeyIsOneField = true;
pkeyFieldSpecified = false;
}
}
if ( fd != null )
pkeyIsOneField = true;
pkeyStuffInitialized = true;
}
catch ( Exception ex ) {
DOLUtils.getDefaultLogger().log(Level.SEVERE, "enterprise.deployment.backend.invalidDescriptorMappingFailure",
new Object[] {ex.toString()});
throw new EJBException(ex);
}
}
/**
* @return true if the primary key of this object is one field
* in its class and the type of the field is not a primitive type.
* True for EJBs if the primkey-field deployment descriptor element
* is specified, or if a container-inserted pk field is used.
*/
public boolean primaryKeyIsOneField()
{
if ( !pkeyStuffInitialized )
initPkeyInfo();
return pkeyIsOneField;
}
/**
* @return false if the primkey-field is not specified and pk class = Object
*/
public boolean primaryKeyIsSpecified()
{
if( !pkeyStuffInitialized )
initPkeyInfo();
return pkeyFieldSpecified;
}
/**
* @return true if the primkey-field is not specified all fields of
* pkey class are Java primitive types.
*/
public boolean primaryKeyFieldsAllPrimitive()
{
if( !pkeyStuffInitialized )
initPkeyInfo();
return pkeyFieldsAllPrimitive;
}
/**
* Get this bean's primary key class.
* For EJBs, this is EjbEntityDescriptor.getPrimaryKeyClassName(),
*/
public Class getPrimaryKeyClass()
{
if ( !pkeyStuffInitialized )
initPkeyInfo();
return primaryKeyClass;
}
// May return null for Join objects when called at/before deployment
public Class getPersistentClass()
{
if ( persistentClass == null ) {
persistentClass = getClass(parentDesc.getEjbClassName());
}
return persistentClass;
}
public Class getStateClass()
{
if ( stateClass == null ) {
stateClass = getPersistentClass();
if ( parentDesc.isEJB20() ) {
if( !Modifier.isAbstract(stateClass.getModifiers()) ) {
throw new EJBException("2.x CMP bean class "
+ stateClass.getName() + " must be decleared abstract "
+ "or cmp-version for the corresponding bean must be set to 1.x.");
}
String stateClassName = parentDesc.getStateImplClassName();
stateClass = getClass(stateClassName);
}
}
return stateClass;
}
private Class getClass(String className)
{
try {
return getEjbBundleDescriptor().getClassLoader().loadClass(className);
} catch ( Exception ex ) {
//if ( debug ) ex.printStackTrace();
throw new EJBException(ex);
}
}
/**
* Set the array of PersistentFieldInfo objects representing the
* foreign key fields of this bean.
*/
public void setFkeyFields(PersistentFieldInfo[] fkeyFields) {
this.fkeyFields = fkeyFields;
fieldInfoInitialized = false;
persFieldInfo = null;
super.changed();
}
/**
* Return the array of PersistentFieldInfo objects for the
* foreign key fields of this bean.
*/
public PersistentFieldInfo[] getFkeyFields() {
if ( !fieldInfoInitialized )
initializeFieldInfo();
return this.fkeyFields;
}
/**
* Return the array of PersistentFieldInfo objects for the
* CMP + foreign key fields
*/
public PersistentFieldInfo[] getPersistentFieldInfo()
{
if ( !fieldInfoInitialized )
initializeFieldInfo();
return persFieldInfo;
}
/**
* Return the PersistentFieldInfo object for the given CMP/fkey field
*/
public PersistentFieldInfo getPersistentFieldInfoByName(String fieldName)
{
if ( !fieldInfoInitialized )
initializeFieldInfo();
for ( int i=0; i<persFieldInfo.length; i++ ) {
if ( persFieldInfo[i].name.equals(fieldName) )
return persFieldInfo[i];
}
throw new EJBException("PersistentFieldInfo not found for field "+fieldName);
}
/**
* Return the array of PersistentFieldInfo objects for the CMP fields
* which are not primary keys + foreign key fields.
*/
public PersistentFieldInfo[] getNonPkeyPersFieldInfo()
{
if ( !fieldInfoInitialized )
initializeFieldInfo();
return persNoPkeyFieldInfo;
}
/**
* Return the array of PersistentFieldInfo objects for the pkey fields.
*/
public PersistentFieldInfo[] getPkeyFieldInfo()
{
if ( !fieldInfoInitialized )
initializeFieldInfo();
return pkeyFieldInfo;
}
/**
* Return PersistentFieldInfo object for the given pkey field.
*/
public PersistentFieldInfo getPkeyFieldInfoByName(String fieldName)
{
if ( !fieldInfoInitialized )
initializeFieldInfo();
for ( int i=0; i<pkeyFieldInfo.length; i++ ) {
if ( pkeyFieldInfo[i].name.equals(fieldName) )
return pkeyFieldInfo[i];
}
throw new EJBException("PersistentFieldInfo not found for pkey field "+fieldName);
}
/**
* @return an array of all Field objects in the primary key class.
* Returns null if primaryKeyIsOneField() == true.
*/
public Field[] getPkeyClassFields()
{
if ( !fieldInfoInitialized )
initializeFieldInfo();
return pkeyClassPkeyFields;
}
// Initialize persFieldInfo, pkeyFieldInfo, persNoPkeyFieldInfo,
// pkeyClassPkeyFields
private void initializeFieldInfo()
{
if ( !pkeyStuffInitialized )
initPkeyInfo();
int cmpFieldCount = cmpFields.size();
if (cmpFieldCount==0) {
throw new EJBException("No cmp field defined for CMP EJB " + parentDesc.getName());
}
int fkeyCount = 0;
if ( fkeyFields != null )
fkeyCount = fkeyFields.length;
persFieldInfo = new PersistentFieldInfo[cmpFieldCount + fkeyCount];
// Add CMP fields
int fcount = 0;
Iterator itr = cmpFields.iterator();
while ( itr.hasNext() ) {
persFieldInfo[fcount] = new PersistentFieldInfo();
persFieldInfo[fcount].name =((FieldDescriptor)itr.next()).getName();
fcount++;
}
// Add foreign key fields
if ( fkeyFields != null ) {
for ( int i=0; i<fkeyFields.length; i++ ) {
persFieldInfo[fcount] = fkeyFields[i];
fcount++;
}
}
// sort persFieldInfo in alphabetical order
for ( int i=persFieldInfo.length-1; i>0; i-- ) {
for ( int j=0; j<i; j++ ) {
if ( persFieldInfo[j].name.compareTo(persFieldInfo[j+1].name)
> 0 ) {
PersistentFieldInfo tmp = persFieldInfo[j];
persFieldInfo[j] = persFieldInfo[j+1];
persFieldInfo[j+1] = tmp;
}
}
}
// Initialize pkeyFieldInfo[] and persNoPkeyFieldInfo[]
// They contain the same PersistentFieldInfo objects as persFieldInfo.
pkeyFieldInfo = new PersistentFieldInfo[pkeyFields.size()];
if ( pkeyFieldSpecified ) {
// check if PK class has public non-persistent fields
StringBuffer nonPersFieldsInPK = new StringBuffer();
for ( Iterator it=pkeyFields.iterator(); it.hasNext(); ) {
FieldDescriptor fd = (FieldDescriptor)it.next();
boolean isPersistent = false;
for ( int i=0; i<persFieldInfo.length; i++ ) {
if ( fd.getName().equals(persFieldInfo[i].name) ) {
isPersistent = true;
break;
}
}
if ( !isPersistent ) {
// if not the first non-persistent field
if ( nonPersFieldsInPK.length() != 0 ) {
nonPersFieldsInPK.append(", ");
}
nonPersFieldsInPK.append(fd.getName());
}
}
if ( nonPersFieldsInPK.length() != 0 ) {
throw new EJBException(localStrings.getLocalString(
"enterprise.deployment.pkhasnopersistentfields",
"CMP bean [{0}], primary key class [{1}] has " +
"public non-persistent field(s) [{2}].",
new Object[] {getParentDescriptor().getName(),
getPrimaryKeyClass().getName(),
nonPersFieldsInPK.toString()}));
}
persNoPkeyFieldInfo = new PersistentFieldInfo[persFieldInfo.length -
pkeyFieldInfo.length];
int pkeyCount = 0;
int noPkeyCount = 0;
for ( int i=0; i<persFieldInfo.length; i++ ) {
boolean isPkey = false;
for ( Iterator it=pkeyFields.iterator(); it.hasNext(); ) {
FieldDescriptor fd = (FieldDescriptor)it.next();
if ( fd.getName().equals(persFieldInfo[i].name) ) {
isPkey = true;
break;
}
}
if ( isPkey )
pkeyFieldInfo[pkeyCount++] = persFieldInfo[i];
else
persNoPkeyFieldInfo[noPkeyCount++] = persFieldInfo[i];
}
}
if ( pkeyIsOneField && pkeyFieldSpecified) {
// Initialize pkey field type. This is needed for
// beans with no pkey field (i.e. PM-generated pkey)
// because they wont have get/set for the pkey in the bean class
// so the getCMPFieldType() below will bomb if type is not set.
// Note: pkeyFieldInfo and persFieldInfo arrays share the same
// PersistentFieldInfo objects.
pkeyFieldInfo[0].type = getPrimaryKeyClass();
}
// Initialize Java types in persFieldInfo
for ( int i=0; i<persFieldInfo.length; i++ ) {
// fill type for CMP fields if not there
// (fkey types will already be filled)
if ( persFieldInfo[i].type == null )
persFieldInfo[i].type = getCMPFieldType(persFieldInfo[i].name);
}
// When called from Deploytool, the bean class is abstract,
// and doesnt have the CMP fields: they will be in the generated
// code after deployment. So all the java.lang.reflect.Field
// values in PersistentFieldInfo can only be initialized at runtime.
try {
if ( persistentClass != null
&& !Modifier.isAbstract(persistentClass.getModifiers()) ) {
for ( int i=0; i<persFieldInfo.length; i++ ) {
persFieldInfo[i].field = getField(getStateClass(),
persFieldInfo[i].name);
}
}
// Initialize pkeyClassPkeyFields
if ( !pkeyIsOneField && primaryKeyClass != null
&& !Modifier.isAbstract(primaryKeyClass.getModifiers()) ) {
pkeyClassPkeyFields = new Field[pkeyFieldInfo.length];
for ( int i=0; i<pkeyFieldInfo.length; i++ ) {
pkeyClassPkeyFields[i] = primaryKeyClass.getField(
pkeyFieldInfo[i].name);
}
}
} catch ( NoSuchFieldException ex ) {
if(DOLUtils.getDefaultLogger().isLoggable(Level.FINE)) {
DOLUtils.getDefaultLogger().log(Level.FINE, ex.toString(), ex);
}
throw new EJBException(ex);
}
fieldInfoInitialized = true;
}
private Field getField(final Class c, final String name)
throws NoSuchFieldException
{
// This privileged block is needed because this code can be
// called when application code is on the stack, which is not
// allowed to reflect on private members of classes.
Field field = (Field)java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction() {
public Object run() {
try {
// this is needed for EJB2.0 CMP beans whose
// generated fields are private.
return c.getDeclaredField(name);
}
catch ( NoSuchFieldException ex ) {
return null;
}
}
}
);
if ( field == null )
field = c.getField(name);
return field;
}
/**
* @return the Class object corresponding to the type of the given
* CMP field.
*/
public Class getTypeFor(String field)
{
return getCMPFieldType(field);
}
private Class getCMPFieldType(String field)
{
Class pclass = getPersistentClass();
if ( Modifier.isAbstract(pclass.getModifiers()) ) {
// An EJB2.0 CMP bean : field is a virtual field
String javaBeanName = capitalize(field);
String getter = "get"+javaBeanName;
try {
Method method = pclass.getMethod(getter, (Class[]) null);
return method.getReturnType();
} catch ( Exception ex ) {
throw new RuntimeException("Cannot find accessor " + getter + " for CMP field "+field);
}
}
else {
// field is a Java field in state class
try {
Field f = getField(getStateClass(), field);
return f.getType();
} catch ( NoSuchFieldException ex ) {
throw new RuntimeException("Cant find CMP field "+field+" in class "+getStateClass().getName());
}
}
}
// called from CMPClassGenerator too
public static String capitalize(String name)
{
// EJB2.0 proposed final draft says that CMP/CMR field names
// must begin with a lower case letter.
if ( Character.isUpperCase(name.charAt(0)) ) {
throw new javax.ejb.EJBException("CMP/CMR field "+name+" must start with a lower case character.");
}
else {
char chars[] = name.toCharArray();
chars[0] = Character.toUpperCase(chars[0]);
return new String(chars);
}
}
public void setQueryFor(MethodDescriptor method, QueryDescriptor query)
{
queries.put(method, query);
}
public QueryDescriptor getQueryFor(MethodDescriptor method)
{
return (QueryDescriptor)queries.get(method);
}
public void removeQueryFor(MethodDescriptor method)
{
queries.remove(method);
}
public void setQueryFor(Method method, QueryDescriptor query)
{
// Use our own method equality check. This prevents problems
// when a different classloader was used to load the input
// method. Also note that two methods with the same name and
// signature on *different* interfaces are considered EQUAL.
// This matches the spec requirement that the same finder
// method defined on the LocalHome and RemoteHome has only
// ONE query-method declaration in the deployment descriptor.
//
MethodDescriptor md = new MethodDescriptor(method,"");
setQueryFor(md, query);
}
public QueryDescriptor getQueryFor(Method method)
{
// Use our own method equality check. See setQueryFor comment
// for more details.
MethodDescriptor md = new MethodDescriptor(method,"");
return (QueryDescriptor) queries.get(md);
}
/**
* Get all methods for which setQueryFor was done
* @return a Set of Methods
*/
public Set getQueriedMethods()
{
return queries.keySet();
}
/**
* Get all HomeIntf.find* and EJBClass.ejbSelect* methods
* for which an EJB-QL query could possibly be set.
* @return a Set of Methods
*/
public Set getAllPossibleQueriedMethods()
{
if ( allQueriedMethods == null )
initializeAllQueriedMethods();
return allQueriedMethods;
}
private void initializeAllQueriedMethods()
{
allQueriedMethods = new HashSet();
// add all ejbSelect* methods in persistentClass
Method[] beanMethods = getPersistentClass().getMethods();
for ( int i=0; i<beanMethods.length; i++ ) {
if ( beanMethods[i].getName().startsWith("ejbSelect") ) {
allQueriedMethods.add(new MethodDescriptor(beanMethods[i], MethodDescriptor.EJB_BEAN));
}
}
// add all finders in Home/LocalHome intf, findByPrimaryKey too
if ( parentDesc.isRemoteInterfacesSupported() ) {
Class homeIntf = getClass(parentDesc.getHomeClassName());
Method[] homeMethods = homeIntf.getMethods();
for ( int i=0; i<homeMethods.length; i++ ) {
String name = homeMethods[i].getName();
if ( name.startsWith("find")
&& !name.equals("findByPrimaryKey") ) {
allQueriedMethods.add(new MethodDescriptor(homeMethods[i], MethodDescriptor.EJB_HOME));
}
}
}
if ( parentDesc.isLocalInterfacesSupported() ) {
Class homeIntf = getClass(parentDesc.getLocalHomeClassName());
Method[] homeMethods = homeIntf.getMethods();
for ( int i=0; i<homeMethods.length; i++ ) {
String name = homeMethods[i].getName();
if ( name.startsWith("find")
&& !name.equals("findByPrimaryKey") ) {
allQueriedMethods.add(new MethodDescriptor(homeMethods[i], MethodDescriptor.EJB_LOCALHOME));
}
}
}
}
/**
* Return my formatted string representation.
*/
public void print(StringBuffer toStringBuffer) {
super.print(toStringBuffer);
toStringBuffer.append("\n Entity descriptor");
toStringBuffer.append("\n cmpFields ").append(cmpFields);
}
}
|