Author: hlship
Date: Fri Sep 26 01:22:37 2008
New Revision: 699214
URL: http://svn.apache.org/viewvc?rev=699214&view=rev
Log:
TAP5-231: Unify injection; allow @Inject annotation on fields of service
implementations
Added:
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/FieldInjectionViaInject.java
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/FieldInjectionViaInjectService.java
Modified:
tapestry/tapestry5/trunk/src/site/apt/index.apt
tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/Inject.java
tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/InjectService.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ActionLink.xdoc
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.xdoc
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/annotations/CommitAfter.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ConstructorServiceCreator.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/configuration.apt
tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/decorator.apt
tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/index.apt
tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/module.apt
tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/overview.apt
tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/provider.apt
tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt
tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/shadow.apt
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/InternalUtilsTest.java
Modified: tapestry/tapestry5/trunk/src/site/apt/index.apt
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/src/site/apt/index.apt?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/src/site/apt/index.apt Fri Sep 26 01:22:37 2008
@@ -32,26 +32,6 @@
Tapestry is released under the Apache Software Licence 2.0.
-Roadmap
-
- Tapestry is, of course, an open-source project, with all the work coming
from unpaid volunteers. That being said, our rough timeline is as follows:
-
- * 5.0 Release Candidate in September 2008
-
- * 5.0 Final Release shortly thereafter
-
- * 5.1 development to follow, on a much shorter release schedule
-
- * ... but we are all open source developers working on our own time and
schedules are hard to pin down. Please be patient. Tapestry 5
- is increasingly stable. Further "stable" with Tapestry usually refers to
names of interfaces and methods, not to code quality,
- which is always very, very high.
-
- * We are now finally edging towards a release candidate, with most work
being documentation, minor bug fixes and modest
- enhancements. Note that Tapestry team members have been using Tapestry 5
to build production applications, and those
- experiences are being applied back to the framework to improve its
quality: we're finding rough edges and we're
- smoothing them out.
-
- []
Third Party Libraries, Tutorials and Resources
@@ -61,6 +41,8 @@
*--+--+--+
| <<Name>> | <<Author>> | <<Description>>
*--+--+--+
+| {{{http://code.google.com/p/tapestry5-appfuse/}AppFuse for Tapestry 5}} |
Serge Eby | Application template, with basic authentication and Hibernate and
Spring integration pre-configured |
+*--+--+--+
| {{{http://equanda.org/equanda-tapestry5/}equanda-tapestry5}} | Joachim Van
der Auwera | Components useful for building enterprise applications.
Includes Accordion, Tabs, Formtraversal. Amongst other things, these focus on
easy input of data without the need for a mouse.
*--+--+--+
| {{{http://code.google.com/p/gc-tapestry-components/}Godcode Components}} |
Chris Lewis | A mixed collection of components providing simple but
time-saving functionality, as well as more exotic ones; built on top of the
prototype and script.aculo.us javascript libraries. |
@@ -86,6 +68,10 @@
New And Of Note
+ * The Inject and InjectService annotations may now be used on fields of
service implementations or other
+ objects constructed by the IoC container. In the past, injections only
occured through method or
+ constructor parameters.
+
* Tapestry now bundles Prototype version 1.6.0.2.
* New methods have been added to
Modified:
tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/Inject.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/Inject.java?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/Inject.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/Inject.java
Fri Sep 26 01:22:37 2008
@@ -15,9 +15,7 @@
package org.apache.tapestry5.ioc.annotations;
import java.lang.annotation.Documented;
-import java.lang.annotation.ElementType;
-import static java.lang.annotation.ElementType.FIELD;
-import static java.lang.annotation.ElementType.PARAMETER;
+import static java.lang.annotation.ElementType.*;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
@@ -35,6 +33,10 @@
* This is most often used in conjunction with [EMAIL PROTECTED]
org.apache.tapestry5.ioc.annotations.Value} annotation when
* injecting a string, as normally, the String would be matched as the service
id.
* <p/>
+ * For service implementations, module classes, and other objects constructed
via [EMAIL PROTECTED]
+ * org.apache.tapestry5.ioc.ObjectLocator#autobuild(Class)}, this annotation
indicates that an injection is desired on
+ * the field, as with fields of a Tapestry component.
+ * <p/>
* In terms of the IoC container, the Inject annotation is only used on
parameters to service builder methods (and
* contributor and decorator methods) and on module class constructors.
constructors. However, inside Tapestry
* components (<em>and only inside components</em>), it may be applied to
fields. On fields that require injection, the
@@ -46,7 +48,7 @@
* @see org.apache.tapestry5.ioc.ObjectProvider
*/
@Target(
- { PARAMETER, FIELD, ElementType.CONSTRUCTOR })
+ {PARAMETER, FIELD, CONSTRUCTOR})
@Retention(RUNTIME)
@Documented
public @interface Inject
Modified:
tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/InjectService.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/InjectService.java?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/InjectService.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-annotations/src/main/java/org/apache/tapestry5/ioc/annotations/InjectService.java
Fri Sep 26 01:22:37 2008
@@ -15,21 +15,24 @@
package org.apache.tapestry5.ioc.annotations;
import java.lang.annotation.Documented;
+import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.PARAMETER;
import java.lang.annotation.Retention;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Target;
[EMAIL PROTECTED](PARAMETER)
[EMAIL PROTECTED](RUNTIME)
[EMAIL PROTECTED]
+
/**
- * Annotation used with parameters of service builder methods to identify the
service to be injected
- * into the service builder method via the parameter. In many cases the
- * [EMAIL PROTECTED] org.apache.tapestry5.ioc.annotations.Inject} annotation
is more flexible or appropriate.
- *
- *
+ * Annotation used with parameters of service builder methods to identify the
service to be injected into the service
+ * builder method via the parameter. In many cases the [EMAIL PROTECTED]
org.apache.tapestry5.ioc.annotations.Inject} annotation is
+ * more flexible or appropriate.
+ * <p/>
+ * This annotation may also be used with fields of service implementation
classes, modules, or other objects constructed
+ * via [EMAIL PROTECTED]
org.apache.tapestry5.ioc.ObjectLocator#autobuild(Class)}.
*/
[EMAIL PROTECTED]({PARAMETER, FIELD})
[EMAIL PROTECTED](RUNTIME)
[EMAIL PROTECTED]
public @interface InjectService
{
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ActionLink.xdoc
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ActionLink.xdoc?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ActionLink.xdoc
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/ActionLink.xdoc
Fri Sep 26 01:22:37 2008
@@ -13,9 +13,9 @@
<source><![CDATA[
public class Account
{
- private long _id;
+ private long id;
- private String _userName;
+ private String userName;
// etc., etc., ...
@@ -54,23 +54,23 @@
public class ViewAccount
{
@Persist
- private Account _account;
+ private Account account;
@InjectPage
- private AccountsSummary _accountsSummaryPage;
+ private AccountsSummary accountsSummaryPage;
@Inject
- private AccountDAO _accountDAO;
+ private AccountDAO accountDAO;
- public Account getAccount() { return _account; }
+ public Account getAccount() { return account; }
Object onActionFromDelete(long accountId)
{
- _accountDAO.delete(accountId);
+ accountDAO.delete(accountId);
- _accountsSummaryPage.setMessage(String.format("Account #%d has been
deleted.", accountId));
+ accountsSummaryPage.setMessage(String.format("Account #%d has been
deleted.", accountId));
- return _accountsSummaryPage;
+ return accountsSummaryPage;
}
}
]]></source>
@@ -115,11 +115,11 @@
Object onActionFromDelete(long companyId, long accountId)
{
- _accountDAO.delete(companyId, accountId);
+ accountDAO.delete(companyId, accountId);
- _accountsSummaryPage.setMessage(String.format("Account #%d has been
deleted.", accountId));
+ accountsSummaryPage.setMessage(String.format("Account #%d has been
deleted.", accountId));
- return _accountsSummaryPage;
+ return accountsSummaryPage;
}]]></source>
<p>
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.xdoc
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.xdoc?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.xdoc
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/corelib/components/BeanEditForm.xdoc
Fri Sep 26 01:22:37 2008
@@ -42,30 +42,30 @@
<source><![CDATA[
public class User
{
- private long _id;
+ private long id;
- private String _firstName;
+ private String firstName;
- private String _lastName;
+ private String lastName;
- private int _age;
+ private int age;
- public long getId() { return _id; }
+ public long getId() { return id; }
@NonVisual
- public void setId(long id) { _id = id; }
+ public void setId(long id) { this.id = id; }
- public String getFirstName() { return _firstName; }
+ public String getFirstName() { return firstName; }
- public void setFirstName(String firstName) { _firstName = firstName; }
+ public void setFirstName(String firstName) { this.firstName = firstName; }
- public String getLastName() { return _lastName; }
+ public String getLastName() { return lastName; }
- public void setLastName(String lastName) { _lastName = lastName; }
+ public void setLastName(String lastName) { this.lastName = lastName; }
- public int getAge() { return _age; }
+ public int getAge() { return age; }
- public void setAge(int age) { _age = age; }
+ public void setAge(int age) { this.age = age; }
}]]></source>
<p>The @NonVisual annotation prevents the id from being
displayed.</p>
@@ -144,24 +144,24 @@
public class CreateUser
{
@Persist
- private User _user;
+ private User user;
@Inject
- private UserDAO _userDAO;
+ private UserDAO userDAO;
public User getUser()
{
- return _user;
+ return user;
}
public void setUser(User user)
{
- _user = user;
+ this.user = user;
}
Object onSuccess()
{
- _userDAO.add(_user);
+ userDAO.add(user);
return UserAdmin.class;
}
@@ -205,19 +205,19 @@
<source><![CDATA[
@Validate("required")
- public String getFirstName() { return _firstName; }
+ public String getFirstName() { return firstName; }
- public void setFirstName(String firstName) { _firstName = firstName; }
+ public void setFirstName(String firstName) { this.firstName = firstName; }
@Validate("required")
- public String getLastName() { return _lastName; }
+ public String getLastName() { return lastName; }
- public void setLastName(String lastName) { _lastName = lastName; }
+ public void setLastName(String lastName) { this.lastName = lastName; }
@Validate("min=18,max=99")
- public int getAge() { return _age; }
+ public int getAge() { return age; }
- public void setAge(int age) { _age = age; }]]> </source>
+ public void setAge(int age) { this.age = age; }]]> </source>
<p>
The new @Validate annotations added to the first name and
last name properties indicates that a
@@ -300,7 +300,7 @@
<p>
You can accomplish the same thing by changing the order of the
- getter methods in the bean class. The default order for
properties is not alphabetical,
+ getter methods in the bean class. The default order for
properties is not alphabetical,
it is the order of the getter methods.
</p>
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/resources/org/apache/tapestry5/tapestry.js
Fri Sep 26 01:22:37 2008
@@ -597,23 +597,6 @@
$T(element).zone = zoneDiv;
- var successHandler = function(transport)
- {
- var reply = transport.responseJSON;
-
- // Find the zone id for the element, and from there, the
Tapestry.Zone object
- // responsible for the zone. This is evaluated late so that zone
can be dynamically
- // changed on the client. Sorry about the confusion; there's the
zone <div>, and
- // there's the Tapestry.Zone object. Perhaps should rename
Tapestry.Zone
- // to Tapestry.ZoneManager?
-
- var zoneId = $T(element).zone;
- var zoneObject = $T(zoneId).zone;
-
- zoneObject.show(reply.content);
-
- Tapestry.processScriptInReply(reply);
- };
if (element.tagName == "FORM")
{
@@ -627,6 +610,14 @@
element.observe(Tapestry.FORM_PROCESS_SUBMIT_EVENT, function()
{
+ var successHandler = function(transport)
+ {
+ var zoneId = $T(element).zone;
+ var zoneObject = $T(zoneId).zone;
+
+ zoneObject.processReply(transport.responseJSON);
+ };
+
element.sendAjaxRequest({ onSuccess : successHandler });
});
@@ -637,7 +628,16 @@
element.observe("click", function(event)
{
- Tapestry.ajaxRequest(element.href, successHandler);
+ // Find the zone id for the element, and from there, the
Tapestry.Zone object
+ // responsible for the zone. This is evaluated late so that zone
can be dynamically
+ // changed on the client. Sorry about the confusion; there's the
zone <div>, and
+ // there's the Tapestry.Zone object. Perhaps should rename
Tapestry.Zone
+ // to Tapestry.ZoneManager?
+
+ var zoneId = $T(element).zone;
+ var zoneObject = $T(zoneId).zone;
+
+ zoneObject.updateFromURL(element.href);
Event.stop(event);
});
@@ -1211,6 +1211,33 @@
var func = this.element.visible() ? this.updateFunc : this.showFunc;
func.call(this, this.element);
+ },
+
+ /**
+ * Invoked with a reply (i.e., transport.responseJSON), this updates the
zone's div
+ * and processes any JavaScript in the reply. The response should have a
+ * content key, and may have script, scripts and stylesheets keys.
+ * @param reply response in JSON format appropriate to a Tapestry.Zone
+ */
+ processReply : function(reply)
+ {
+ this.show(reply.content);
+
+ Tapestry.processScriptInReply(reply);
+ },
+
+ /** Initiates an Ajax request to update this zone by sending a request
+ * to the URL. Expects the correct JSON reply (wth keys content, etc.).
+ * @param URL component event request URL
+ */
+ updateFromURL : function (URL)
+ {
+ var successHandler = function(transport)
+ {
+ this.processReply(transport.responseJSON);
+ }.bind(this);
+
+ Tapestry.ajaxRequest(URL, successHandler);
}
});
Modified:
tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/annotations/CommitAfter.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/annotations/CommitAfter.java?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/annotations/CommitAfter.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-hibernate/src/main/java/org/apache/tapestry5/hibernate/annotations/CommitAfter.java
Fri Sep 26 01:22:37 2008
@@ -23,7 +23,7 @@
/**
* Marks a method of a service (or a component method) as transactional: the
active transaction should [EMAIL PROTECTED]
* org.apache.tapestry5.hibernate.HibernateSessionManager#commit() commit}
after invoking the method. Runtime
- * exceptions will abort the transaction, checked exceptions will <also
commit> the transaction.
+ * exceptions will abort the transaction, checked exceptions will <em>also
commit</em> the transaction.
*
* @see org.apache.tapestry5.hibernate.HibernateTransactionDecorator
*/
Modified:
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ConstructorServiceCreator.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ConstructorServiceCreator.java?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ConstructorServiceCreator.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ConstructorServiceCreator.java
Fri Sep 26 01:22:37 2008
@@ -49,7 +49,11 @@
if (logger.isDebugEnabled())
logger.debug(IOCMessages.invokingConstructor(creatorDescription));
- return constructor.newInstance(parameters);
+ Object result = constructor.newInstance(parameters);
+
+ InternalUtils.injectIntoFields(result, resources);
+
+ return result;
}
catch (InvocationTargetException ite)
{
Modified:
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/ModuleImpl.java
Fri Sep 26 01:22:37 2008
@@ -338,7 +338,11 @@
constructor.getParameterTypes(),
constructor.getParameterAnnotations());
- return constructor.newInstance(parameterValues);
+ Object result = constructor.newInstance(parameterValues);
+
+ InternalUtils.injectIntoFields(result, locator);
+
+ return result;
}
catch (InvocationTargetException ex)
{
Modified:
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/RegistryImpl.java
Fri Sep 26 01:22:37 2008
@@ -174,7 +174,6 @@
for (Class marker : serviceDef.getMarkers())
InternalUtils.addToMapList(markerToServiceDef, marker,
serviceDef);
-
}
moduleToServiceDefs.put(module, moduleServiceDefs);
@@ -454,7 +453,6 @@
def.contribute(module, locator, validating);
}
-
}
private <T> void addToUnorderedConfiguration(Configuration<T>
configuration, Class<T> valueType,
@@ -781,7 +779,11 @@
Object[] parameters =
InternalUtils.calculateParametersForConstructor(constructor, this, empty);
- return clazz.cast(constructor.newInstance(parameters));
+ Object result = constructor.newInstance(parameters);
+
+ InternalUtils.injectIntoFields(result, this);
+
+ return clazz.cast(result);
}
catch (InvocationTargetException ite)
{
Modified:
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/util/InternalUtils.java
Fri Sep 26 01:22:37 2008
@@ -30,6 +30,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
@@ -166,14 +167,14 @@
}
@SuppressWarnings("unchecked")
- private static Object calculateParameterValue(Class parameterType, final
Annotation[] parameterAnnotations,
- ObjectLocator locator,
Map<Class, Object> parameterDefaults)
+ private static Object calculateInjection(Class injectionType, final
Annotation[] annotations,
+ ObjectLocator locator, Map<Class,
Object> defaults)
{
AnnotationProvider provider = new AnnotationProvider()
{
public <T extends Annotation> T getAnnotation(Class<T>
annotationClass)
{
- return findAnnotation(parameterAnnotations, annotationClass);
+ return findAnnotation(annotations, annotationClass);
}
};
@@ -186,7 +187,7 @@
{
String serviceId = is.value();
- return locator.getService(serviceId, parameterType);
+ return locator.getService(serviceId, injectionType);
}
// In the absence of @InjectService, try some autowiring. First, does
the
@@ -194,7 +195,7 @@
if (provider.getAnnotation(Inject.class) == null)
{
- Object result = parameterDefaults.get(parameterType);
+ Object result = defaults.get(injectionType);
if (result != null) return result;
}
@@ -202,7 +203,7 @@
// Otherwise, make use of the MasterObjectProvider service to resolve
this type (plus
// any other information gleaned from additional annotation) into the
correct object.
- return locator.getObject(parameterType, provider);
+ return locator.getObject(injectionType, provider);
}
public static Object[] calculateParametersForMethod(Method method,
ObjectLocator locator,
@@ -223,7 +224,7 @@
return calculateParameters(locator, parameterDefaults, parameterTypes,
annotations);
}
- public static Object[] calculateParameters(ObjectLocator locator,
Map<Class, Object> parameterDefaults,
+ public static Object[] calculateParameters(ObjectLocator locator,
Map<Class, Object> defaults,
Class[] parameterTypes,
Annotation[][] parameterAnnotations)
{
int parameterCount = parameterTypes.length;
@@ -232,14 +233,86 @@
for (int i = 0; i < parameterCount; i++)
{
- parameters[i] = calculateParameterValue(parameterTypes[i],
parameterAnnotations[i], locator,
- parameterDefaults);
+ parameters[i] = calculateInjection(parameterTypes[i],
parameterAnnotations[i], locator,
+ defaults);
}
return parameters;
}
/**
+ * Injects into the fields (of all visibilities) when the [EMAIL
PROTECTED] org.apache.tapestry5.ioc.annotations.Inject} or
+ * [EMAIL PROTECTED] org.apache.tapestry5.ioc.annotations.InjectService}
annotations are present.
+ *
+ * @param object to be initialized
+ * @param locator used to resolve external dependencies
+ */
+ public static void injectIntoFields(Object object, ObjectLocator locator)
+ {
+ Class clazz = object.getClass();
+
+ while (clazz != Object.class)
+ {
+
+ Field[] fields = clazz.getDeclaredFields();
+
+ for (final Field f : fields)
+ {
+ // Ignore all static fields.
+
+ if (Modifier.isStatic(f.getModifiers())) continue;
+
+ AnnotationProvider ap = new AnnotationProvider()
+ {
+ public <T extends Annotation> T getAnnotation(Class<T>
annotationClass)
+ {
+ return f.getAnnotation(annotationClass);
+ }
+ };
+
+
+ InjectService is = ap.getAnnotation(InjectService.class);
+
+ if (is != null)
+ {
+ inject(object, f, locator.getService(is.value(),
f.getType()));
+ continue;
+ }
+
+ if (ap.getAnnotation(Inject.class) != null)
+ {
+ inject(object, f, locator.getObject(f.getType(), ap));
+ continue;
+ }
+
+ // Ignore fields that do not have the necessary annotation.
Should we ignore static
+ // fields?
+ }
+
+
+ clazz = clazz.getSuperclass();
+ }
+ }
+
+ private synchronized static void inject(Object target, Field field, Object
value)
+ {
+ try
+ {
+ if (!field.isAccessible()) field.setAccessible(true);
+
+ field.set(target, value);
+
+ // Is there a need to setAccessible back to false?
+ }
+ catch (Exception ex)
+ {
+ throw new RuntimeException(String.format("Unable to set field '%s'
of %s to %s: %s",
+ field.getName(), target,
value,
+ toMessage(ex)));
+ }
+ }
+
+ /**
* Joins together some number of elements to form a comma separated list.
*/
public static String join(List elements)
Modified:
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/services/TapestryIOCModule.java
Fri Sep 26 01:22:37 2008
@@ -17,7 +17,6 @@
import org.apache.tapestry5.ioc.*;
import static org.apache.tapestry5.ioc.IOCConstants.PERTHREAD_SCOPE;
import org.apache.tapestry5.ioc.annotations.Marker;
-import org.apache.tapestry5.ioc.annotations.Value;
import org.apache.tapestry5.ioc.internal.services.*;
import org.apache.tapestry5.ioc.util.TimeInterval;
@@ -84,9 +83,8 @@
}
/**
- * Contributes "DefaultProvider", ordered last, that delegates to [EMAIL
PROTECTED] ObjectLocator#getService(Class)}.
- * <p/>
- * Contributes "Value", which injects values (not services) triggered by
the [EMAIL PROTECTED] Value} annotation.
+ * <dl> <dt>Value</dt> <dd>Supports the [EMAIL PROTECTED]
org.apache.tapestry5.ioc.annotations.Value} annotation</dd>
+ * <dt>Symbol</dt> <dd>Supports the [EMAIL PROTECTED]
org.apache.tapestry5.ioc.annotations.Symbol} annotations</dd> </dl>
*/
public static void
contributeMasterObjectProvider(OrderedConfiguration<ObjectProvider>
configuration,
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/configuration.apt
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/configuration.apt?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/configuration.apt
(original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/configuration.apt Fri
Sep 26 01:22:37 2008
@@ -50,7 +50,7 @@
Like service builder and service decorator methods, we can inject services
if we like:
+------+
- public static void
contributFileServicerDispatcher(MappedConfiguration<String,FileServicer>
configuration,
+ public static void
contributeFileServicerDispatcher(MappedConfiguration<String,FileServicer>
configuration,
@InjectService("TextFileServicer") FileServicer textFileServicer,
@@ -65,7 +65,7 @@
service configuration:
+------+
- public static void
contributeFileServicerDispatcher(MappedConfiguration<String,FileServicer>
configuration)
+ public static void
contributeFileServicerDispatcher(MappedConfiguration<String,FileServicer>
configuration)
{
configuration.add("doc", new WordFileServicer());
configuration.add("ppt", new PowerPointFileServicer());
@@ -221,7 +221,7 @@
be unique. When conflicts occur, Tapestry will log warnings (identifying
the source, in terms of invoked methods, of
the conflict), and ignore the conflicting value.
- The value may not be null.
+ Neither the key nor the value may be null.
For mapped configurations where the key type is String, a
{{{apidocs/org/apache/tapestry5/ioc/util/CaseInsensitiveMap.html}CaseInsensitiveMap}}
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/decorator.apt
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/decorator.apt?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/decorator.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/decorator.apt Fri Sep 26
01:22:37 2008
@@ -149,8 +149,13 @@
you may identify which services are to be decorated.
The value specified in the Match annotation is one or more patterns. These
patterns
- are used to match services. In a pattern, a "*" at the start or end of a
string
- match zero or more characters.
+ are used to match services. Patterns take two forms: glob patterns and
regular expressions.
+
+ In a glob pattern, a "*" at the start or end of a string
+ will match zero or more characters. Regular expressions provide
a lot more matching power, but require
+ a more involved syntax.
+
+ In either case, the matching is case insensitive.
For example, to target all the services in your module:
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/index.apt
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/index.apt?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/index.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/index.apt Fri Sep 26
01:22:37 2008
@@ -100,7 +100,7 @@
Another goal is "developer friendliness". This is a true cross-cutting
concern, and one not likely to be packaged
into an aspect any time soon. The Tapestry IoC framework is designed to be
easy to use and easy to understand.
- Further, when things go wrong, it actively attempts to help you by
+ Further, when things go wrong, it actively attempts to help you via
comprehensive checks and carefully composed error messages. Further,
all user-visible objects implement
{{{http://howardlewisship.com/blog/2003/08/importance-of-tostring.html}a
reasonable toString() method}},
@@ -119,7 +119,7 @@
to test and maintain existing code. Too often in the world of software
development, the need to add functionality
trumps all, and testing and maintenance is deferred ... until too late.
- IoC containers is general, and Tapestry IoC very specifically, exist to
address this issue, to provide the foundations
+ IoC containers in general, and Tapestry IoC very specifically, exist to
address this issue, to provide the foundations
for balancing the need to quickly add functionality against the need to test
new functionality and maintain
existing functionality. IoC containers provide the means to break large,
complex, monolithic blocks into light, small, testable
pieces.
@@ -201,7 +201,10 @@
the service interface. The first time a method is invoked on the proxy, the
full service (consisting of the core service implementation wrapped with any
interceptors) is
constructed. This occurs in a completely <<thread-safe>> manner.
Just-in-time instantiation allows for more complex, more finely grained
networks of services, and improves
startup time.
-
+
+ Instantiating a service, injecting dependencies, and decorating the service
are all parts of service <<realization>>, the point
+ at which a service transitions from virtual (just a proxy) to real (fully
instantiated and ready to operate).
+
Services define a <<scope>> that controls when the service is constructed,
as well as its visibility. The default scope is <<singleton>>, meaning a single
global instance created as needed. Other scopes allow service
implementations to be bound to the current thread (i.e., the current
request in a servlet application).
@@ -210,4 +213,7 @@
dependencies can be <<injected>> into a service builder method and provided,
from there, to a service implementation via
its constructor, or via methods on the service implementation. These may
also be referred to as <<collaborators>>, especially
in the context of writing unit tests.
-
\ No newline at end of file
+
+ The <<point of Injection>> is a field, method parameter, or constructor
parameter that receives an injected value.
+ The type of service (or other dependency) is determined by the type of the
field or parameter. Often,
+ annotations further identify what is to be injected, or in the case of field
injection, that an injection is required.
\ No newline at end of file
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/module.apt
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/module.apt?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/module.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/module.apt Fri Sep 26
01:22:37 2008
@@ -299,3 +299,37 @@
The annotation can be applied to method and constructor parameters, for use
within the IoC container. It can also be applied
to fields, though this is specific to the Tapestry web framework.
+
+Field Injection
+
+ The {{{../apidocs/org/apache/tapestry5/annotations/Inject.html}Inject}}
+ and
{{{../apidocs/org/apache/tapestry5/annotations/InjectService.html}InjectService}}
+ annotations may be used on instance fields of a module class, as an
alternative
+ to passing dependencies of the module in via the constructor.
+
+ Caution: injection via fields uses reflection to make the fields accessible.
In
+ addition, it may not be as thread-safe as using the constructor to assign to
+ final fields.
+
+ Using this style, the previous example of a module class may be
+ rewritten:
+
++-----------------------------------------------------------------------------------+
+public class MyModule
+{
+ @Inject
+ private JobScheduler scheduler;
+
+ @Inject
+ private FileSystem fileSystem;
+
+ public Indexer build()
+ {
+ IndexerImpl indexer = new IndexerImpl(fileSystem);
+
+ scheduler.scheduleDailyJob(indexer);
+
+ return indexer;
+ }
+}
++-----------------------------------------------------------------------------------+
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/overview.apt
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/overview.apt?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/overview.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/overview.apt Fri Sep 26
01:22:37 2008
@@ -112,12 +112,13 @@
Dependency Injection is a key part of <realization>: this is how a service
is provided with the other services it needs to operate. For example,
a Data Access Object service may be injected with a ConnectionPool service.
- In Tapestry, injection occurs exclusively through constructors. Other
frameworks support a mix of constructor injection, property injection (i.e.,
invoking setter methods)
- and method injection (invoking arbitrary methods to pass in dependencies).
Tapestry focuses exclusively on constructor injection, and emphasizes
+ In Tapestry, injection occurs through constructors, through parameters to
service builder methods, or through direct injection
+ into fields. Tapestry prefers constructor injection, as this emphasizes
that dependencies should be stored in <<final>> variables. This is the best
approach towards ensuring thread safety.
In any case, injection "just happens". Tapestry finds the constructor of
your class and analyzes the parameters to determine what to pass in. In some
cases,
- it uses just the parameter type to find a match, in other cases, annotations
on the parameters may also be used.
+ it uses just the parameter type to find a match, in other cases, annotations
on the parameters may also be used. It also scans through the fields
+ of your service implementation class to identify which should have injected
values written into them.
Why can't I just use <<<new>>>?
@@ -321,7 +322,7 @@
A new TableMetricProducer instance is created and contributed in. We could
contribute as many producers as we like here. Other modules could also
define a contributeMetricScheduler() method and contribute their own
MetricProducer instances.
- Meanwhile, the QueueWriterImpl class no longer needs the _instance variable
or getInstance() method, and the TableMetricProducer
+ Meanwhile, the QueueWriterImpl class no longer needs the <<<instance>>>
variable or getInstance() method, and the TableMetricProducer
only needs a single constructor.
Advantages of IoC: Summary
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/provider.apt
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/provider.apt?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/provider.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/provider.apt Fri Sep 26
01:22:37 2008
@@ -16,10 +16,12 @@
forming a {{{command.html}chain of command}}. The commands in the chain may
provide an object
based on the parameter type, or based on additional annotations on the
parameter.
- There are two built-in object providers:
+ There are several built-in object providers:
* Check for
{{{../apidocs/org/apache/tapestry5/ioc/annotations/Value.html}Value}} annotation
-
+
+ * Check for
{{{../apidocs/org/apache/tapestry5/ioc/annotations/Symbol.html}Symbol}}
annotation
+
* Check for a <unique> service in the Registry whose service interface
matches the parameter type
[]
@@ -43,8 +45,22 @@
Here, the MyService service requires a configuration of a number of seconds.
The value is supplied as a symbol, with a factory default that may be
overwritten
- with an application default.
-
+ with an application default.
+
+ Usually, the symbol reference is only part of the string, i.e.
<<<@Value("$\{report.dir}/$\{report.name}.txt")>>>
+
[EMAIL PROTECTED] Annotation Provider
+
+ This is closely related to the @Value annotation approach, except that the
annotation
+ directly specifies a symbol name.
+
++----+
+ public MyService build(@Symbol("max-seconds") long maxSeconds)
+ {
+ return new MyServiceImpl(maxSeconds);
+ }
++----+
+
Service Provider
This is always that last object provider checked.
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/service.apt Fri Sep 26
01:22:37 2008
@@ -155,13 +155,15 @@
It's pretty unlikely that your service will be able to operate in a total
vacuum. It will
have other dependencies.
- Dependencies are provided to a service in one of three ways:
+ Dependencies are provided to a service in one of several ways:
* As parameters to the service builder method
* As parameters to the service implementation class' constructor (for
autobuilt services)
* As parameters passed to the constructor of the service's module builder
(cached inside instance variables)
+
+ * Directly into fields of the service implementation
[]
@@ -383,7 +385,37 @@
Understanding why this is a bad idea involves a long detour into inner
details of the Java Memory Model.
The short form is that other threads may end up invoking methods on the
IndexerImpl instance, and its fields
(even though they are final, even though they appear to already have been
set) may be uninitialized.
-
+
+Field Injection
+
+ The {{{../apidocs/org/apache/tapestry5/annotations/Inject.html}Inject}}
+ and
{{{../apidocs/org/apache/tapestry5/annotations/InjectService.html}InjectService}}
+ annotations may be used on instance fields of a service implementation
class, as an alternative
+ to passing dependencies of the service implementation in via the constructor.
+
+ Note that only dependencies are settable this way; if you want resources,
including
+ the service's {{{configuration.html}configuration}}, you must pass those
through the constructor.
+ You <are> free to mix and match, injecting partially with field injection
and partially
+ with constructor injection.
+
+ Caution: injection via fields uses reflection to make the fields accessible.
In
+ addition, it may not be as thread-safe as using the constructor to assign to
+ final fields.
+
++---+
+package org.example.myapp.services;
+
+import org.apache.tapestry5.ioc.annotations.InjectService;
+
+public class IndexerImpl implements Indexer
+{
+ @InjectService("FileSystem")
+ private FileSystem fileSystem;
+
+ . . .
+}
++---+
+
Defining Service Scope
Each service has a <lifecycle> that controls when the service implementation
is instantiated.
@@ -455,7 +487,7 @@
In addition, it is possible to specify the scope when binding the service:
+----+
- bind(MyServiceInterface.class, MyServiceImpl.class).scope("perthread");
+ bind(MyServiceInterface.class,
MyServiceImpl.class).scope(IOCConstants.PERTHREAD_SCOPE);
+----+
@@ -477,7 +509,7 @@
Many services may be annotated with @EagerLoad; the order in which services
are created is not defined.
- With the perthread lifecycle, the service builder method will not be invoked
(this won't happen until
+ With the perthread scope, the service builder method will not be invoked
(this won't happen until
a service method is invoked), but the decorators for
the service will be created.
@@ -514,7 +546,11 @@
See also {{{configuration.html}service configuration}} for additional
special cases
of resources that can be injected.
-
+
+ Note: resources may not be injected into fields, they are injectable only via
+ method or constructor parameters.
+
+
Example:
+-----------------------------------------------------------------------------------+
@@ -642,7 +678,7 @@
With Tapestry IoC, this is not even considered a special case:
+-----------------------------------------------------------------------------------+
- public static Indexer build(JobScheduler scheduler, FileSystem fileSystem)
+ public static Indexer buildIndexer(JobScheduler scheduler, FileSystem
fileSystem)
{
IndexerImpl indexer = new IndexerImpl(fileSystem);
@@ -651,7 +687,7 @@
return indexer;
}
- public static Indexed build(Indexer indexer)
+ public static FileSystem buildFileSystem(Indexer indexer)
{
return new FileSystemImpl(indexer);
}
Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/shadow.apt
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/shadow.apt?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/shadow.apt (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/site/apt/shadow.apt Fri Sep 26
01:22:37 2008
@@ -16,23 +16,25 @@
+----+
public Request build()
{
- return _shadowBuilder.build(_requestGlobals, "request", Request.class);
+ return shadowBuilder.build(requestGlobals, "request", Request.class);
}
+----+
+ (shadowBuilder and requestGlobals are injected into the module builder
instance)
+
This can be thought of as similar to:
+----+
public Request build()
{
- return _requestGlobals.getRequest();
+ return requestGlobals.getRequest();
}
+----+
However there is a <critical> difference between the two: a shadow property
is <re-evaluated on each method invocation>.
In the former case, the Request service will always obtain the current value
of the request property from the
per-thread RequestGlobals service. The second example is more than likely
broken, since it will expose whatever
- value is in the request property of the RequestGlobals <at the time the
Request service implementation is created>.
+ value is in the request property of the RequestGlobals <at the time the
Request service implementation is realized>.
Notice that in this example, the Request service is a normal singleton. This
service can be freely injected
into any service throughout the framework or application. Invoking methods
on this service will always delegate
@@ -46,11 +48,11 @@
A typical method is implemented as (approximately):
+----+
-private RequestGlobals _source;
+private final RequestGlobals source;
public String getParameter(String name)
{
- return _source.getRequest().getParameter(name);
+ return source.getRequest().getParameter(name);
}
+----+
Added:
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/FieldInjectionViaInject.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/FieldInjectionViaInject.java?rev=699214&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/FieldInjectionViaInject.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/FieldInjectionViaInject.java
Fri Sep 26 01:22:37 2008
@@ -0,0 +1,31 @@
+// Copyright 2008 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.tapestry5.ioc.internal.util;
+
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.ioc.services.Builtin;
+import org.apache.tapestry5.ioc.services.SymbolSource;
+
+public class FieldInjectionViaInject
+{
+ @Inject
+ @Builtin
+ private SymbolSource symbolSource;
+
+ public SymbolSource getSymbolSource()
+ {
+ return symbolSource;
+ }
+}
Added:
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/FieldInjectionViaInjectService.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/FieldInjectionViaInjectService.java?rev=699214&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/FieldInjectionViaInjectService.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/FieldInjectionViaInjectService.java
Fri Sep 26 01:22:37 2008
@@ -0,0 +1,29 @@
+// Copyright 2008 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.tapestry5.ioc.internal.util;
+
+import org.apache.tapestry5.ioc.annotations.InjectService;
+
+public class FieldInjectionViaInjectService
+{
+ @InjectService("FredService")
+ private Runnable fred;
+
+ public Runnable getFred() { return fred; }
+
+ @Override
+ public String toString()
+ { return "<FieldInjectionViaInjectService>"; }
+}
Modified:
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/InternalUtilsTest.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/InternalUtilsTest.java?rev=699214&r1=699213&r2=699214&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/InternalUtilsTest.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/util/InternalUtilsTest.java
Fri Sep 26 01:22:37 2008
@@ -14,12 +14,20 @@
package org.apache.tapestry5.ioc.internal.util;
+import org.apache.tapestry5.ioc.AnnotationProvider;
import org.apache.tapestry5.ioc.Locatable;
import org.apache.tapestry5.ioc.Location;
+import org.apache.tapestry5.ioc.ObjectLocator;
import org.apache.tapestry5.ioc.annotations.Inject;
import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newMap;
import static org.apache.tapestry5.ioc.internal.util.InternalUtils.toList;
+import org.apache.tapestry5.ioc.services.Builtin;
+import org.apache.tapestry5.ioc.services.SymbolSource;
import org.apache.tapestry5.ioc.test.IOCTestCase;
+import org.easymock.EasyMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.isA;
+import org.easymock.IAnswer;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -454,4 +462,86 @@
"Constructor protected
org.apache.tapestry5.ioc.internal.util.InternalUtilsTest$PublicInnerClass() is
not public and may not be used for autobuilding an instance of the class.");
}
}
+
+ @Test
+ public void inject_service_annotation_on_field()
+ {
+ ObjectLocator ol = mockObjectLocator();
+ FieldInjectionViaInjectService target = new
FieldInjectionViaInjectService();
+ Runnable fred = mockRunnable();
+
+ train_getService(ol, "FredService", Runnable.class, fred);
+
+ replay();
+
+ InternalUtils.injectIntoFields(target, ol);
+
+ assertSame(target.getFred(), fred);
+
+ verify();
+ }
+
+ @Test
+ public void inject_annotation_on_field()
+ {
+ ObjectLocator ol = mockObjectLocator();
+ FieldInjectionViaInject target = new FieldInjectionViaInject();
+ final SymbolSource ss = mockSymbolSource();
+
+ IAnswer answer = new IAnswer()
+ {
+ public Object answer() throws Throwable
+ {
+ Object[] args = EasyMock.getCurrentArguments();
+
+ AnnotationProvider ap = (AnnotationProvider) args[1];
+
+ // Verify that annotations on the field are accessible.
+
+ assertNotNull(ap.getAnnotation(Builtin.class));
+
+ return ss;
+ }
+ };
+
+ expect(ol.getObject(eq(SymbolSource.class),
isA(AnnotationProvider.class))).andAnswer(answer);
+
+ replay();
+
+ InternalUtils.injectIntoFields(target, ol);
+
+ assertSame(target.getSymbolSource(), ss);
+
+ verify();
+ }
+
+ @Test
+ public void exception_injecting_into_field()
+ {
+ ObjectLocator ol = mockObjectLocator();
+ FieldInjectionViaInjectService target = new
FieldInjectionViaInjectService();
+
+ // It's very hard to come up with a value that causes an error when
assigned. We have to break
+ // a lot of rules.
+
+ ol.getService("FredService", Runnable.class);
+ EasyMock.expectLastCall().andReturn("NotTheRightType");
+
+ replay();
+
+ try
+ {
+ InternalUtils.injectIntoFields(target, ol);
+
+ unreachable();
+ }
+ catch (Exception ex)
+ {
+ assertMessageContains(ex,
+ "Unable to set field 'fred' of
<FieldInjectionViaInjectService> to NotTheRightType");
+ }
+
+
+ verify();
+ }
}