SEC-1165: Relax the requirement that the ObjectIdentity "type" be a Java class. Modified ObjectIdentity, changing the javaType property to "type" which is now a plain String. Also removes the requirement that the class be present on the classpath when creating the ObjectIdentityImpl instance (e.g. in the case of a permissions administration app which doesn't actually use the domain classes itself).

This commit is contained in:
Luke Taylor 2009-06-09 00:17:45 +00:00
parent 0473cfbfc0
commit 39d76d5b5f
6 changed files with 41 additions and 42 deletions

View File

@ -34,37 +34,36 @@ import org.springframework.util.ClassUtils;
public class ObjectIdentityImpl implements ObjectIdentity { public class ObjectIdentityImpl implements ObjectIdentity {
//~ Instance fields ================================================================================================ //~ Instance fields ================================================================================================
private Class<?> javaType; private final String type;
private Serializable identifier; private Serializable identifier;
//~ Constructors =================================================================================================== //~ Constructors ===================================================================================================
public ObjectIdentityImpl(String javaType, Serializable identifier) { public ObjectIdentityImpl(String type, Serializable identifier) {
Assert.hasText(javaType, "Java Type required"); Assert.hasText(type, "Type required");
Assert.notNull(identifier, "identifier required"); Assert.notNull(identifier, "identifier required");
try {
this.javaType = ClassUtils.forName(javaType, ClassUtils.getDefaultClassLoader());
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Unable to load javaType: " + javaType, e);
}
this.identifier = identifier; this.identifier = identifier;
this.type = type;
} }
/**
* Constructor which uses the name of the supplied class as the <tt>type</tt> property.
*/
public ObjectIdentityImpl(Class<?> javaType, Serializable identifier) { public ObjectIdentityImpl(Class<?> javaType, Serializable identifier) {
Assert.notNull(javaType, "Java Type required"); Assert.notNull(javaType, "Java Type required");
Assert.notNull(identifier, "identifier required"); Assert.notNull(identifier, "identifier required");
this.javaType = javaType; this.type = javaType.getName();
this.identifier = identifier; this.identifier = identifier;
} }
/** /**
* Creates the <code>ObjectIdentityImpl</code> based on the passed * Creates the <code>ObjectIdentityImpl</code> based on the passed
* object instance. The passed object must provide a <code>getId()</code> * object instance. The passed object must provide a <code>getId()</code>
* method, otherwise an exception will be thrown. The object passed will * method, otherwise an exception will be thrown.
* be considered the {@link #javaType}, so if more control is required, * <p>
* an alternate constructor should be used instead. * The class name of the object passed will be considered the {@link #type}, so if more control is required,
* a different constructor should be used.
* *
* @param object the domain object instance to create an identity for. * @param object the domain object instance to create an identity for.
* *
@ -73,12 +72,13 @@ public class ObjectIdentityImpl implements ObjectIdentity {
public ObjectIdentityImpl(Object object) throws IdentityUnavailableException { public ObjectIdentityImpl(Object object) throws IdentityUnavailableException {
Assert.notNull(object, "object cannot be null"); Assert.notNull(object, "object cannot be null");
this.javaType = ClassUtils.getUserClass(object.getClass()); Class<?> typeClass = ClassUtils.getUserClass(object.getClass());
type = typeClass.getName();
Object result; Object result;
try { try {
Method method = this.javaType.getMethod("getId", new Class[] {}); Method method = typeClass.getMethod("getId", new Class[] {});
result = method.invoke(object, new Object[] {}); result = method.invoke(object, new Object[] {});
} catch (Exception e) { } catch (Exception e) {
throw new IdentityUnavailableException("Could not extract identity from object " + object, e); throw new IdentityUnavailableException("Could not extract identity from object " + object, e);
@ -123,15 +123,15 @@ public class ObjectIdentityImpl implements ObjectIdentity {
} }
} }
return javaType.equals(other.javaType); return type.equals(other.type);
} }
public Serializable getIdentifier() { public Serializable getIdentifier() {
return identifier; return identifier;
} }
public Class<?> getJavaType() { public String getType() {
return javaType; return type;
} }
/** /**
@ -141,7 +141,7 @@ public class ObjectIdentityImpl implements ObjectIdentity {
*/ */
public int hashCode() { public int hashCode() {
int code = 31; int code = 31;
code ^= this.javaType.hashCode(); code ^= this.type.hashCode();
code ^= this.identifier.hashCode(); code ^= this.identifier.hashCode();
return code; return code;
@ -150,7 +150,7 @@ public class ObjectIdentityImpl implements ObjectIdentity {
public String toString() { public String toString() {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append(this.getClass().getName()).append("["); sb.append(this.getClass().getName()).append("[");
sb.append("Java Type: ").append(this.javaType.getName()); sb.append("Type: ").append(this.type);
sb.append("; Identifier: ").append(this.identifier).append("]"); sb.append("; Identifier: ").append(this.identifier).append("]");
return sb.toString(); return sb.toString();

View File

@ -365,11 +365,10 @@ public final class BasicLookupStrategy implements LookupStrategy {
Set parentsToLookup = (Set) jdbcTemplate.query(sql, Set parentsToLookup = (Set) jdbcTemplate.query(sql,
new PreparedStatementSetter() { new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) public void setValues(PreparedStatement ps) throws SQLException {
throws SQLException {
for (int i = 0; i < objectIdentities.length; i++) { for (int i = 0; i < objectIdentities.length; i++) {
// Determine prepared statement values for this iteration // Determine prepared statement values for this iteration
String javaType = objectIdentities[i].getJavaType().getName(); String javaType = objectIdentities[i].getType();
// No need to check for nulls, as guaranteed non-null by ObjectIdentity.getIdentifier() interface contract // No need to check for nulls, as guaranteed non-null by ObjectIdentity.getIdentifier() interface contract
String identifier = objectIdentities[i].getIdentifier().toString(); String identifier = objectIdentities[i].getIdentifier().toString();

View File

@ -72,7 +72,7 @@ public class JdbcAclService implements AclService {
//~ Methods ======================================================================================================== //~ Methods ========================================================================================================
public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) { public List<ObjectIdentity> findChildren(ObjectIdentity parentIdentity) {
Object[] args = {parentIdentity.getIdentifier(), parentIdentity.getJavaType().getName()}; Object[] args = {parentIdentity.getIdentifier(), parentIdentity.getType()};
List<ObjectIdentity> objects = jdbcTemplate.query(selectAclObjectWithParent, args, List<ObjectIdentity> objects = jdbcTemplate.query(selectAclObjectWithParent, args,
new RowMapper<ObjectIdentity>() { new RowMapper<ObjectIdentity>() {
public ObjectIdentity mapRow(ResultSet rs, int rowNum) throws SQLException { public ObjectIdentity mapRow(ResultSet rs, int rowNum) throws SQLException {

View File

@ -148,7 +148,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
*/ */
protected void createObjectIdentity(ObjectIdentity object, Sid owner) { protected void createObjectIdentity(ObjectIdentity object, Sid owner) {
Long sidId = createOrRetrieveSidPrimaryKey(owner, true); Long sidId = createOrRetrieveSidPrimaryKey(owner, true);
Long classId = createOrRetrieveClassPrimaryKey(object.getJavaType(), true); Long classId = createOrRetrieveClassPrimaryKey(object.getType(), true);
jdbcTemplate.update(insertObjectIdentity, classId, object.getIdentifier(), sidId, Boolean.TRUE); jdbcTemplate.update(insertObjectIdentity, classId, object.getIdentifier(), sidId, Boolean.TRUE);
} }
@ -161,15 +161,15 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
* *
* @return the primary key or null if not found * @return the primary key or null if not found
*/ */
protected Long createOrRetrieveClassPrimaryKey(Class<?> clazz, boolean allowCreate) { protected Long createOrRetrieveClassPrimaryKey(String type, boolean allowCreate) {
List<Long> classIds = jdbcTemplate.queryForList(selectClassPrimaryKey, new Object[] {clazz.getName()}, Long.class); List<Long> classIds = jdbcTemplate.queryForList(selectClassPrimaryKey, new Object[] {type}, Long.class);
if (!classIds.isEmpty()) { if (!classIds.isEmpty()) {
return classIds.get(0); return classIds.get(0);
} }
if (allowCreate) { if (allowCreate) {
jdbcTemplate.update(insertClass, new Object[] {clazz.getName()}); jdbcTemplate.update(insertClass, type);
Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(), Assert.isTrue(TransactionSynchronizationManager.isSynchronizationActive(),
"Transaction must be running"); "Transaction must be running");
return new Long(jdbcTemplate.queryForLong(classIdentityQuery)); return new Long(jdbcTemplate.queryForLong(classIdentityQuery));
@ -290,7 +290,7 @@ public class JdbcMutableAclService extends JdbcAclService implements MutableAclS
protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) { protected Long retrieveObjectIdentityPrimaryKey(ObjectIdentity oid) {
try { try {
return new Long(jdbcTemplate.queryForLong(selectObjectIdentityPrimaryKey, return new Long(jdbcTemplate.queryForLong(selectObjectIdentityPrimaryKey,
new Object[] {oid.getJavaType().getName(), oid.getIdentifier()})); new Object[] {oid.getType(), oid.getIdentifier()}));
} catch (DataAccessException notFound) { } catch (DataAccessException notFound) {
return null; return null;
} }

View File

@ -21,11 +21,11 @@ import java.io.Serializable;
* Represents the identity of an individual domain object instance. * Represents the identity of an individual domain object instance.
* *
* <p> * <p>
* As implementations of <tt>ObjectIdentity</tt> are used as the key to represent * As implementations of <tt>ObjectIdentity</tt> are used as the key to represent
* domain objects in the ACL subsystem, it is essential that implementations provide * domain objects in the ACL subsystem, it is essential that implementations provide
* methods so that object-equality rather than reference-equality can be relied upon * methods so that object-equality rather than reference-equality can be relied upon
* reliably. In other words, the ACL subsystem can consider two * reliably. In other words, the ACL subsystem can consider two
* <tt>ObjectIdentity</tt>s equal if <tt>identity1.equals(identity2)</tt>, rather than * <tt>ObjectIdentity</tt>s equal if <tt>identity1.equals(identity2)</tt>, rather than
* reference-equality of <tt>identity1==identity2</tt>. * reference-equality of <tt>identity1==identity2</tt>.
* </p> * </p>
* *
@ -46,10 +46,10 @@ public interface ObjectIdentity extends Serializable {
/** /**
* Obtains the actual identifier. This identifier must not be reused to represent other domain objects with * Obtains the actual identifier. This identifier must not be reused to represent other domain objects with
* the same <tt>javaType</tt>. * the same <tt>javaType</tt>.
* *
* <p>Because ACLs are largely immutable, it is strongly recommended to use * <p>Because ACLs are largely immutable, it is strongly recommended to use
* a synthetic identifier (such as a database sequence number for the primary key). Do not use an identifier with * a synthetic identifier (such as a database sequence number for the primary key). Do not use an identifier with
* business meaning, as that business meaning may change in the future such change will cascade to the ACL * business meaning, as that business meaning may change in the future such change will cascade to the ACL
* subsystem data.</p> * subsystem data.</p>
* *
* @return the identifier (unique within this <tt>javaType</tt>; never <tt>null</tt>) * @return the identifier (unique within this <tt>javaType</tt>; never <tt>null</tt>)
@ -57,12 +57,12 @@ public interface ObjectIdentity extends Serializable {
Serializable getIdentifier(); Serializable getIdentifier();
/** /**
* Obtains the Java type represented by the domain object. The Java type can be an interface or a class, but is * Obtains the "type" metadata for the domain object. This will often be a Java type name (an interface or a class)
* most often the domain object implementation class. * &ndash; traditionally it is the name of the domain object implementation class.
* *
* @return the Java type of the domain object (never <tt>null</tt>) * @return the "type" of the domain object (never <tt>null</tt>).
*/ */
Class<?> getJavaType(); String getType();
/** /**
* @return a hash code representation of the <tt>ObjectIdentity</tt> * @return a hash code representation of the <tt>ObjectIdentity</tt>

View File

@ -64,7 +64,7 @@ public class ObjectIdentityImplTests {
public void gettersReturnExpectedValues() throws Exception { public void gettersReturnExpectedValues() throws Exception {
ObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1)); ObjectIdentity obj = new ObjectIdentityImpl(DOMAIN_CLASS, Long.valueOf(1));
assertEquals(Long.valueOf(1), obj.getIdentifier()); assertEquals(Long.valueOf(1), obj.getIdentifier());
assertEquals(MockIdDomainObject.class, obj.getJavaType()); assertEquals(MockIdDomainObject.class.getName(), obj.getType());
} }
@Test @Test
@ -106,9 +106,9 @@ public class ObjectIdentityImplTests {
} }
} }
@Test(expected=IllegalStateException.class) @Test(expected=IllegalArgumentException.class)
public void testConstructorInvalidClassParameter() throws Exception { public void constructorRejectsInvalidTypeParameter() throws Exception {
new ObjectIdentityImpl("not.a.Class", Long.valueOf(1)); new ObjectIdentityImpl("", Long.valueOf(1));
} }
@Test @Test