You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2006/08/02 17:21:49 UTC
svn commit: r428021 - in /tapestry/tapestry5/tapestry-core/trunk/src:
main/java/org/apache/tapestry/internal/ioc/
main/java/org/apache/tapestry/internal/test/
main/java/org/apache/tapestry/ioc/services/
main/resources/org/apache/tapestry/ioc/ main/reso...
Author: hlship
Date: Wed Aug 2 08:21:47 2006
New Revision: 428021
URL: http://svn.apache.org/viewvc?rev=428021&view=rev
Log:
Create a service to encapsulate JavaBean property access.
Added:
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapter.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapterImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/IOCServicesMessages.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAccess.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAccessImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapter.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapterImpl.java
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/ioc/
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/ioc/services/
tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/ioc/services/IOCServicesStrings.properties
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/IOCUtilitiesTest.java
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/services/PropertyAccessImplTest.java
Modified:
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java
tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java?rev=428021&r1=428020&r2=428021&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/ioc/IOCUtilities.java Wed Aug 2 08:21:47 2006
@@ -16,11 +16,15 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import java.util.Map;
import org.apache.tapestry.internal.annotations.Utility;
import org.apache.tapestry.ioc.ServiceLocator;
import org.apache.tapestry.ioc.annotations.InjectService;
+import org.apache.tapestry.util.CollectionFactory;
/**
* Contains static methods used within this package.
@@ -99,5 +103,34 @@
}
return parameters;
+ }
+
+ /** Joins together some number of elements to form a comma separated list. */
+ public static String join(Collection<String> elements)
+ {
+ StringBuffer buffer = new StringBuffer();
+ boolean first = true;
+
+ for (String s : elements)
+ {
+ if (!first)
+ buffer.append(", ");
+
+ buffer.append(s);
+
+ first = false;
+ }
+
+ return buffer.toString();
+ }
+
+ /** Creates a sorted copy of the provided elements, then turns that into a comma separated list. */
+ public static String joinSorted(Collection<String> elements)
+ {
+ List<String> list = CollectionFactory.newList(elements);
+
+ Collections.sort(list);
+
+ return join(list);
}
}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java?rev=428021&r1=428020&r2=428021&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/internal/test/InternalBaseTestCase.java Wed Aug 2 08:21:47 2006
@@ -21,6 +21,8 @@
import org.apache.tapestry.internal.ioc.InternalRegistry;
import org.apache.tapestry.internal.ioc.Module;
import org.apache.tapestry.internal.parser.ComponentTemplate;
+import org.apache.tapestry.ioc.Registry;
+import org.apache.tapestry.ioc.RegistryBuilder;
import org.apache.tapestry.ioc.ServiceDecorator;
import org.apache.tapestry.ioc.ServiceLifecycle;
import org.apache.tapestry.ioc.def.ServiceDef;
@@ -78,5 +80,13 @@
protected final InternalRegistry newInternalRegistry()
{
return newMock(InternalRegistry.class);
+ }
+
+ protected final Registry buildRegistry(Class... moduleClasses)
+ {
+ RegistryBuilder builder = new RegistryBuilder();
+ builder.add(moduleClasses);
+
+ return builder.build();
}
}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapter.java?rev=428021&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapter.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapter.java Wed Aug 2 08:21:47 2006
@@ -0,0 +1,48 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.services;
+
+/**
+ * Organizes all {@link org.apache.tapestry.ioc.services.PropertyAdapter}s for a particular class.
+ *
+ * @author Howard M. Lewis Ship
+ */
+public interface ClassPropertyAdapter
+{
+ /**
+ * Returns the property adapter with the given name, or null if no such adapter exists.
+ */
+ PropertyAdapter getPropertyAdapter(String name);
+
+ /**
+ * Reads the value of a property.
+ *
+ * @throws UnsupportedOperationException
+ * if the property is write only
+ * @throws IllegalArgumentException
+ * if property does not exist
+ */
+ Object get(Object instance, String propertyName);
+
+ /**
+ * Updates the value of a property. *
+ *
+ * @throws UnsupportedOperationException
+ * if the property is read only
+ * @throws IllegalArgumentException
+ * if property does not exist
+ */
+ void set(Object instance, String propertyName, Object value);
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapterImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapterImpl.java?rev=428021&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapterImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/ClassPropertyAdapterImpl.java Wed Aug 2 08:21:47 2006
@@ -0,0 +1,83 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.services;
+
+import java.beans.PropertyDescriptor;
+import java.util.Map;
+
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+import org.apache.tapestry.internal.ioc.IOCUtilities;
+
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public class ClassPropertyAdapterImpl implements ClassPropertyAdapter
+{
+ private final Map<String, PropertyAdapter> _adapters = newMap();
+
+ private final Class _targetClass;
+
+ public ClassPropertyAdapterImpl(Class targetClass, PropertyDescriptor[] descriptors)
+ {
+ _targetClass = targetClass;
+
+ for (PropertyDescriptor pd : descriptors)
+ {
+ PropertyAdapter pa = new PropertyAdapterImpl(pd);
+
+ _adapters.put(pa.getName(), pa);
+ }
+ }
+
+ @Override
+ public String toString()
+ {
+ String names = IOCUtilities.joinSorted(_adapters.keySet());
+
+ return String.format("<ClassPropertyAdaptor %s : %s>", _targetClass.getName(), names);
+ }
+
+ public PropertyAdapter getPropertyAdapter(String name)
+ {
+ return _adapters.get(name);
+ }
+
+ @SuppressNullCheck
+ public Object get(Object instance, String propertyName)
+ {
+ return adaptorFor(propertyName).get(instance);
+ }
+
+ @SuppressNullCheck
+ public void set(Object instance, String propertyName, Object value)
+ {
+ adaptorFor(propertyName).set(instance, value);
+ }
+
+ private PropertyAdapter adaptorFor(String name)
+ {
+ PropertyAdapter pa = _adapters.get(name);
+
+ if (pa == null)
+ throw new IllegalArgumentException(IOCServicesMessages.noSuchProperty(
+ _targetClass,
+ name));
+
+ return pa;
+ }
+
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/IOCServicesMessages.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/IOCServicesMessages.java?rev=428021&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/IOCServicesMessages.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/IOCServicesMessages.java Wed Aug 2 08:21:47 2006
@@ -0,0 +1,50 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.services;
+
+import org.apache.hivemind.Messages;
+import org.apache.hivemind.impl.MessageFormatter;
+import org.apache.tapestry.internal.annotations.Utility;
+
+@Utility
+public class IOCServicesMessages
+{
+ private static final Messages MESSAGES = new MessageFormatter(IOCServicesMessages.class);
+
+ static String readNotSupported(Object instance, String propertyName)
+ {
+ return MESSAGES.format("read-not-supported", instance.getClass().getName(), propertyName);
+ }
+
+ static String writeNotSupported(Object instance, String propertyName)
+ {
+ return MESSAGES.format("write-not-supported", instance.getClass().getName(), propertyName);
+ }
+
+ static String readFailure(String propertyName, Object instance, Throwable cause)
+ {
+ return MESSAGES.format("read-failure", propertyName, instance, cause);
+ }
+
+ static String writeFailure(String propertyName, Object instance, Throwable cause)
+ {
+ return MESSAGES.format("write-failure", propertyName, instance, cause);
+ }
+
+ static String noSuchProperty(Class clazz, String propertyName)
+ {
+ return MESSAGES.format("no-such-property", clazz.getName(), propertyName);
+ }
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAccess.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAccess.java?rev=428021&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAccess.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAccess.java Wed Aug 2 08:21:47 2006
@@ -0,0 +1,58 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.services;
+
+/**
+ * A wrapper around the JavaBean Introspector that allows more manageable access to JavaBean
+ * properties of objects.
+ *
+ * @author Howard M. Lewis Ship
+ */
+public interface PropertyAccess
+{
+ /**
+ * Reads the value of a property.
+ *
+ * @throws UnsupportedOperationException
+ * if the property is write only
+ * @throws IllegalArgumentException
+ * if property does not exist
+ */
+ Object get(Object instance, String propertyName);
+
+ /**
+ * Updates the value of a property.
+ *
+ * @throws UnsupportedOperationException
+ * if the property is read only
+ * @throws IllegalArgumentException
+ * if property does not exist
+ */
+ void set(Object instance, String propertyName, Object value);
+
+ /**
+ * Returns the adapter for a particular object instance. A convienience over invoking
+ * {@link #getAdapter(Class)}.
+ */
+ ClassPropertyAdapter getAdapter(Object instance);
+
+ /**
+ * Returns the adapter used to access properties within the indicated class.
+ */
+ ClassPropertyAdapter getAdapter(Class forClass);
+
+ /** Discards all stored property access information, discarding all created class adapters. */
+ void clear();
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAccessImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAccessImpl.java?rev=428021&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAccessImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAccessImpl.java Wed Aug 2 08:21:47 2006
@@ -0,0 +1,92 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.services;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.util.Map;
+
+import org.apache.tapestry.internal.annotations.Concurrent;
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+
+import static org.apache.tapestry.util.CollectionFactory.newMap;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+@Concurrent
+public class PropertyAccessImpl implements PropertyAccess
+{
+ private final Map<Class, ClassPropertyAdapter> _adapters = newMap();
+
+ @SuppressNullCheck
+ public Object get(Object instance, String propertyName)
+ {
+ return getAdapter(instance).get(instance, propertyName);
+ }
+
+ @SuppressNullCheck
+ public void set(Object instance, String propertyName, Object value)
+ {
+ getAdapter(instance).set(instance, propertyName, value);
+ }
+
+ @Concurrent.Write
+ public void clear()
+ {
+ _adapters.clear();
+ }
+
+ public ClassPropertyAdapter getAdapter(Object instance)
+ {
+ return getAdapter(instance.getClass());
+ }
+
+ @Concurrent.Read
+ public ClassPropertyAdapter getAdapter(Class forClass)
+ {
+ ClassPropertyAdapter result = _adapters.get(forClass);
+
+ if (result == null)
+ result = buildAdapter(forClass);
+
+ return result;
+ }
+
+ /**
+ * Builds a new adapter and updates the _adapters cache. This not only guards access to the
+ * adapter cache, but also serializes access to the Java Beans Introspector, which is not thread
+ * safe.
+ */
+ @Concurrent.Write
+ private ClassPropertyAdapter buildAdapter(Class forClass)
+ {
+ try
+ {
+ BeanInfo info = Introspector.getBeanInfo(forClass);
+
+ ClassPropertyAdapter adapter = new ClassPropertyAdapterImpl(forClass, info
+ .getPropertyDescriptors());
+
+ _adapters.put(forClass, adapter);
+
+ return adapter;
+ }
+ catch (Exception ex)
+ {
+ throw new RuntimeException(ex);
+ }
+ }
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapter.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapter.java?rev=428021&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapter.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapter.java Wed Aug 2 08:21:47 2006
@@ -0,0 +1,79 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.services;
+
+import java.lang.reflect.Method;
+
+/**
+ * Provides access to a single property within a class.
+ *
+ * @author Howard M. Lewis Ship
+ * @see org.apache.tapestry.ioc.services.ClassPropertyAdapter
+ */
+public interface PropertyAdapter
+{
+ /**
+ * Returns the name of the property.
+ */
+ String getName();
+
+ /**
+ * Returns true if the property is readable (i.e., has a getter method).
+ */
+ boolean isRead();
+
+ /**
+ * Returns the method used to read the property, or null if the property is not readable.
+ */
+ public Method getReadMethod();
+
+ /**
+ * Returns true if the property is writeable (i.e., has a setter method).
+ */
+ boolean isUpdate();
+
+ /**
+ * Returns the method used to update the property, or null if the property is not writeable.
+ */
+ public Method getWriteMethod();
+
+ /**
+ * Reads the property value.
+ *
+ * @param instance
+ * to read from
+ * @throws UnsupportedOperationException
+ * if the property is write only
+ */
+ Object get(Object instance);
+
+ /**
+ * Updates the property value. The provided value must not be null if the property type is
+ * primitive, and must otherwise be of the proper type.
+ *
+ * @param instance
+ * to update
+ * @param value
+ * new value for the property
+ * @throws UnsupportedOperationException
+ * if the property is read only
+ */
+ void set(Object instance, Object value);
+
+ /**
+ * Returns the type of the property.
+ */
+ Class getType();
+}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapterImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapterImpl.java?rev=428021&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapterImpl.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/PropertyAdapterImpl.java Wed Aug 2 08:21:47 2006
@@ -0,0 +1,131 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.services;
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.apache.tapestry.internal.annotations.SuppressNullCheck;
+import org.apache.tapestry.util.Defense;
+
+@SuppressNullCheck
+public class PropertyAdapterImpl implements PropertyAdapter
+{
+ private final String _name;
+
+ private final Method _readMethod;
+
+ private final Method _writeMethod;
+
+ private final Class _type;
+
+ public PropertyAdapterImpl(String name, Class type, Method readMethod, Method writeMethod)
+ {
+ _name = Defense.notBlank(name, "name");
+ _type = Defense.notNull(type, "type");
+ _readMethod = readMethod;
+ _writeMethod = writeMethod;
+ }
+
+ public PropertyAdapterImpl(PropertyDescriptor descriptor)
+ {
+ this(descriptor.getName(), descriptor.getPropertyType(), descriptor.getReadMethod(),
+ descriptor.getWriteMethod());
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public Method getReadMethod()
+ {
+ return _readMethod;
+ }
+
+ public Class getType()
+ {
+ return _type;
+ }
+
+ public Method getWriteMethod()
+ {
+ return _writeMethod;
+ }
+
+ public boolean isRead()
+ {
+ return _readMethod != null;
+ }
+
+ public boolean isUpdate()
+ {
+ return _writeMethod != null;
+ }
+
+ public Object get(Object instance)
+ {
+ if (_readMethod == null)
+ throw new UnsupportedOperationException(IOCServicesMessages.readNotSupported(
+ instance,
+ _name));
+
+ Throwable fail = null;
+
+ try
+ {
+ return _readMethod.invoke(instance);
+ }
+ catch (InvocationTargetException ex)
+ {
+ fail = ex.getTargetException();
+ }
+ catch (Exception ex)
+ {
+ fail = ex;
+ }
+
+ throw new RuntimeException(IOCServicesMessages.readFailure(_name, instance, fail));
+ }
+
+ public void set(Object instance, Object value)
+ {
+ if (_writeMethod == null)
+ throw new UnsupportedOperationException(IOCServicesMessages.writeNotSupported(
+ instance,
+ _name));
+
+ Throwable fail = null;
+
+ try
+ {
+ _writeMethod.invoke(instance, value);
+
+ return;
+ }
+ catch (InvocationTargetException ex)
+ {
+ fail = ex.getTargetException();
+ }
+ catch (Exception ex)
+ {
+ fail = ex;
+ }
+
+ throw new RuntimeException(IOCServicesMessages.writeFailure(_name, instance, fail));
+ }
+
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java?rev=428021&r1=428020&r2=428021&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/java/org/apache/tapestry/ioc/services/TapestryIOCModule.java Wed Aug 2 08:21:47 2006
@@ -102,4 +102,13 @@
{
return new ChainBuilderImpl(classFactory);
}
+
+ /**
+ * Services that provides read/write access to JavaBean properties. Encapsulates JavaBean
+ * introspection, including serializing access to the non-thread-safe Introspector object.
+ */
+ public PropertyAccess buildPropertyAccess()
+ {
+ return new PropertyAccessImpl();
+ }
}
Added: tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/ioc/services/IOCServicesStrings.properties
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/ioc/services/IOCServicesStrings.properties?rev=428021&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/ioc/services/IOCServicesStrings.properties (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/main/resources/org/apache/tapestry/ioc/services/IOCServicesStrings.properties Wed Aug 2 08:21:47 2006
@@ -0,0 +1,19 @@
+# Copyright 2006 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+read-not-supported=Class {0} does not provide an accessor ('getter') method for property ''{1}''.
+write-not-supported=Class {0} does not provide an mutator ('setter') method for property ''{1}''.
+read-failure=Error reading property ''{0}'' of {1}: {2}
+write-failure=Error updating property ''{0}'' of {1}: {2}
+no-such-property=Class {0} does not contain a property named ''{1}''.
\ No newline at end of file
Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/IOCUtilitiesTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/IOCUtilitiesTest.java?rev=428021&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/IOCUtilitiesTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/internal/ioc/IOCUtilitiesTest.java Wed Aug 2 08:21:47 2006
@@ -0,0 +1,64 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.internal.ioc;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.tapestry.test.TestBase;
+import org.apache.tapestry.util.CollectionFactory;
+import org.testng.annotations.Test;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public class IOCUtilitiesTest extends TestBase
+{
+ @Test
+ public void join_empty_list()
+ {
+ List<String> empty = CollectionFactory.newList();
+
+ assertEquals(IOCUtilities.join(empty), "");
+ }
+
+ @Test
+ public void join_single()
+ {
+ List<String> single = Arrays.asList("barney");
+
+ assertEquals(IOCUtilities.join(single), "barney");
+ }
+
+ @Test
+ public void join_multiple()
+ {
+ List<String> many = Arrays.asList("fred", "barney", "wilma");
+ assertEquals(IOCUtilities.join(many), "fred, barney, wilma");
+ }
+
+ @Test
+ public void join_sorted()
+ {
+ List<String> unsorted = Arrays.asList("betty", "fred", "barney", "wilma");
+ List<String> copy = CollectionFactory.newList(unsorted);
+
+ assertEquals(IOCUtilities.joinSorted(copy), "barney, betty, fred, wilma");
+
+ // Make sure that joinSorted() doesn't change the input list
+
+ assertEquals(copy, unsorted);
+ }
+}
Modified: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java?rev=428021&r1=428020&r2=428021&view=diff
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java (original)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/IntegrationTest.java Wed Aug 2 08:21:47 2006
@@ -38,14 +38,6 @@
return buildRegistry(TapestryIOCModule.class, FredModule.class, BarneyModule.class);
}
- private Registry buildRegistry(Class... moduleClasses)
- {
- RegistryBuilder builder = new RegistryBuilder();
- builder.add(moduleClasses);
-
- return builder.build();
- }
-
/**
* Along the way, we also test a few other things, such as decorator matching and automatic
* dependency resolution.
Added: tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/services/PropertyAccessImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/services/PropertyAccessImplTest.java?rev=428021&view=auto
==============================================================================
--- tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/services/PropertyAccessImplTest.java (added)
+++ tapestry/tapestry5/tapestry-core/trunk/src/test/java/org/apache/tapestry/ioc/services/PropertyAccessImplTest.java Wed Aug 2 08:21:47 2006
@@ -0,0 +1,368 @@
+// Copyright 2006 The Apache Software Foundation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package org.apache.tapestry.ioc.services;
+
+import java.awt.Image;
+import java.beans.BeanDescriptor;
+import java.beans.BeanInfo;
+import java.beans.EventSetDescriptor;
+import java.beans.MethodDescriptor;
+import java.beans.PropertyDescriptor;
+import java.util.Random;
+
+import org.apache.tapestry.internal.test.InternalBaseTestCase;
+import org.apache.tapestry.ioc.Registry;
+import org.testng.annotations.Test;
+
+/**
+ * @author Howard M. Lewis Ship
+ */
+public class PropertyAccessImplTest extends InternalBaseTestCase
+{
+ private static final String CLASS_NAME = PropertyAccessImplTest.class.getName();
+
+ private PropertyAccess _access = new PropertyAccessImpl();
+
+ private Random _random = new Random();
+
+ public static class Bean
+ {
+ private int _value;
+
+ public int getValue()
+ {
+ return _value;
+ }
+
+ public void setValue(int value)
+ {
+ _value = value;
+ }
+
+ @Override
+ public String toString()
+ {
+ return "PropertyUtilsTestBean";
+ }
+
+ public void setWriteOnly(boolean b)
+ {
+ }
+
+ public String getReadOnly()
+ {
+ return null;
+ }
+ }
+
+ public static class ExceptionBean
+ {
+ public boolean getFailure()
+ {
+ throw new RuntimeException("getFailure");
+ }
+
+ public void setFailure(boolean b)
+ {
+ throw new RuntimeException("setFailure");
+ }
+
+ @Override
+ public String toString()
+ {
+ return "PropertyUtilsExceptionBean";
+ }
+ }
+
+ public static class UglyBean
+ {
+ }
+
+ public static class UglyBeanBeanInfo implements BeanInfo
+ {
+
+ public BeanInfo[] getAdditionalBeanInfo()
+ {
+ return new BeanInfo[0];
+ }
+
+ public BeanDescriptor getBeanDescriptor()
+ {
+ return null;
+ }
+
+ public int getDefaultEventIndex()
+ {
+ return 0;
+ }
+
+ public int getDefaultPropertyIndex()
+ {
+ return 0;
+ }
+
+ public EventSetDescriptor[] getEventSetDescriptors()
+ {
+ return new EventSetDescriptor[0];
+ }
+
+ public Image getIcon(int iconKind)
+ {
+ return null;
+ }
+
+ public MethodDescriptor[] getMethodDescriptors()
+ {
+ return new MethodDescriptor[0];
+ }
+
+ public PropertyDescriptor[] getPropertyDescriptors()
+ {
+ throw new RuntimeException("This is the UglyBean.");
+ }
+
+ }
+
+ public static class BooleanHolder
+ {
+ private boolean _flag;
+
+ public boolean isFlag()
+ {
+ return _flag;
+ }
+
+ public void setFlag(boolean flag)
+ {
+ _flag = flag;
+ }
+ }
+
+ @Test
+ public void simple_read_access()
+ {
+ Bean b = new Bean();
+
+ int value = _random.nextInt();
+
+ b.setValue(value);
+
+ assertEquals(_access.get(b, "value"), value);
+ }
+
+ @Test
+ public void simple_write_access()
+ {
+ Bean b = new Bean();
+
+ int value = _random.nextInt();
+
+ _access.set(b, "value", value);
+
+ assertEquals(b.getValue(), value);
+ }
+
+ @Test
+ public void missing_property()
+ {
+ Bean b = new Bean();
+
+ try
+ {
+ _access.get(b, "zaphod");
+
+ unreachable();
+ }
+ catch (IllegalArgumentException ex)
+ {
+ assertEquals(ex.getMessage(), "Class " + CLASS_NAME + "$Bean does not "
+ + "contain a property named 'zaphod'.");
+ }
+ }
+
+ @Test
+ public void attempt_to_update_read_only_property()
+ {
+ Bean b = new Bean();
+
+ try
+ {
+ _access.set(b, "class", null);
+ unreachable();
+ }
+ catch (UnsupportedOperationException ex)
+ {
+ assertEquals(ex.getMessage(), "Class " + CLASS_NAME
+ + "$Bean does not provide an mutator (setter) method for property 'class'.");
+ }
+ }
+
+ @Test
+ public void attempt_to_read_from_write_only_property()
+ {
+ Bean b = new Bean();
+
+ try
+ {
+ _access.get(b, "writeOnly");
+ unreachable();
+ }
+ catch (UnsupportedOperationException ex)
+ {
+ assertEquals(
+ ex.getMessage(),
+ "Class "
+ + CLASS_NAME
+ + "$Bean does not provide an accessor (getter) method for property 'writeOnly'.");
+ }
+ }
+
+ @Test
+ public void exception_thrown_inside_getter()
+ {
+ ExceptionBean b = new ExceptionBean();
+
+ try
+ {
+ _access.get(b, "failure");
+ unreachable();
+ }
+ catch (RuntimeException ex)
+ {
+ assertEquals(
+ ex.getMessage(),
+ "Error reading property 'failure' of PropertyUtilsExceptionBean: getFailure");
+ }
+ }
+
+ @Test
+ public void exception_thrown_inside_setter()
+ {
+ ExceptionBean b = new ExceptionBean();
+
+ try
+ {
+ _access.set(b, "failure", false);
+ unreachable();
+ }
+ catch (RuntimeException ex)
+ {
+ assertEquals(
+ ex.getMessage(),
+ "Error updating property 'failure' of PropertyUtilsExceptionBean: setFailure");
+ }
+ }
+
+ @Test
+ public void failure_when_introspecting_class()
+ {
+ UglyBean b = new UglyBean();
+
+ try
+ {
+ _access.get(b, "google");
+ unreachable();
+ }
+ catch (RuntimeException ex)
+ {
+ assertEquals(ex.getMessage(), "java.lang.RuntimeException: This is the UglyBean.");
+ }
+ }
+
+ @Test
+ public void clear_wipes_internal_cache()
+ {
+ ClassPropertyAdapter cpa1 = _access.getAdapter(Bean.class);
+ ClassPropertyAdapter cpa2 = _access.getAdapter(Bean.class);
+
+ assertSame(cpa2, cpa1);
+
+ _access.clear();
+
+ ClassPropertyAdapter cpa3 = _access.getAdapter(Bean.class);
+
+ assertNotSame(cpa3, cpa1);
+ }
+
+ @Test
+ public void class_property_adapter_toString()
+ {
+ ClassPropertyAdapter cpa = _access.getAdapter(Bean.class);
+
+ assertEquals(cpa.toString(), "<ClassPropertyAdaptor " + CLASS_NAME
+ + "$Bean : class, readOnly, value, writeOnly>");
+ }
+
+ @Test
+ public void property_adapter_read_only_property()
+ {
+ ClassPropertyAdapter cpa = _access.getAdapter(Bean.class);
+ PropertyAdapter pa = cpa.getPropertyAdapter("readOnly");
+
+ assertTrue(pa.isRead());
+ assertFalse(pa.isUpdate());
+
+ assertNull(pa.getWriteMethod());
+ assertEquals(pa.getReadMethod(), findMethod(Bean.class, "getReadOnly"));
+ }
+
+ @Test
+ public void property_adapter_write_only_property()
+ {
+ ClassPropertyAdapter cpa = _access.getAdapter(Bean.class);
+ PropertyAdapter pa = cpa.getPropertyAdapter("writeOnly");
+
+ assertFalse(pa.isRead());
+ assertTrue(pa.isUpdate());
+
+ assertEquals(pa.getWriteMethod(), findMethod(Bean.class, "setWriteOnly"));
+ assertNull(pa.getReadMethod());
+ }
+
+ @Test
+ public void class_property_adapter_returns_null_for_unknown_property()
+ {
+ ClassPropertyAdapter cpa = _access.getAdapter(Bean.class);
+
+ assertNull(cpa.getPropertyAdapter("google"));
+ }
+
+ @Test
+ public void access_to_property_type()
+ {
+ ClassPropertyAdapter cpa = _access.getAdapter(Bean.class);
+
+ assertEquals(cpa.getPropertyAdapter("value").getType(), int.class);
+ assertEquals(cpa.getPropertyAdapter("readOnly").getType(), String.class);
+ assertEquals(cpa.getPropertyAdapter("writeOnly").getType(), boolean.class);
+ }
+
+ @Test
+ public void integration()
+ {
+ Registry registry = buildRegistry(TapestryIOCModule.class);
+
+ PropertyAccess pa = registry
+ .getService("tapestry.ioc.PropertyAccess", PropertyAccess.class);
+
+ Bean b = new Bean();
+
+ int value = _random.nextInt();
+
+ pa.set(b, "value", value);
+
+ assertEquals(b.getValue(), value);
+ }
+}