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 2005/02/10 16:47:42 UTC
cvs commit: jakarta-tapestry/framework/src/java/org/apache/tapestry AbstractComponent.java
hlship 2005/02/10 07:47:42
Modified: . status.xml
framework/src/test/org/apache/tapestry/enhance
TestEnhancementOperation.java
framework/src/descriptor/META-INF tapestry.enhance.xml
framework/src/java/org/apache/tapestry/enhance
EnhancementOperation.java
EnhancementOperationImpl.java
framework/src/java/org/apache/tapestry
AbstractComponent.java
Added: framework/src/java/org/apache/tapestry/services/impl
ClassObjectProvider.java
framework/src/test/org/apache/tapestry/enhance
TestInjectListenerRegistrationWorker.java
framework/src/java/org/apache/tapestry/enhance
InjectListenerRegistrationWorker.java
Log:
Use enhancement to inject page event registration into components.
Revision Changes Path
1.1 jakarta-tapestry/framework/src/java/org/apache/tapestry/services/impl/ClassObjectProvider.java
Index: ClassObjectProvider.java
===================================================================
// Copyright 2005 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.services.impl;
import org.apache.hivemind.Location;
import org.apache.hivemind.internal.Module;
import org.apache.hivemind.service.ObjectProvider;
/**
* An object provider which interprets its locator as a class name and returns a Class instance.
* Mapped to the "class:" prefix.
* <p>
* TODO: Move to HiveMind 1.1!
*
* @author Howard M. Lewis Ship
* @since 3.1
*/
public class ClassObjectProvider implements ObjectProvider
{
public Object provideObject(Module contributingModule, Class propertyType, String locator,
Location location)
{
// This should be contributingModule.resolveType(locator) in the latest HiveMind.
return contributingModule.getClassResolver().findClass(locator);
}
}
1.70 +1 -0 jakarta-tapestry/status.xml
Index: status.xml
===================================================================
RCS file: /home/cvs/jakarta-tapestry/status.xml,v
retrieving revision 1.69
retrieving revision 1.70
diff -u -r1.69 -r1.70
--- status.xml 10 Feb 2005 14:27:36 -0000 1.69
+++ status.xml 10 Feb 2005 15:47:41 -0000 1.70
@@ -146,6 +146,7 @@
<action type="update" dev="HLS"> Add new ComponentPropertySource service for accessing component meta-data properties. </action>
<action type="fix" dev="HLS" fixes-bug="TAPESTRY-165"> Allow control over attribute name for components in templates. </action>
<action type="update" dev="HLS"> Inject specification into components. </action>
+ <action type="update" dev="HLS"> Use enhancement to inject page event registration into components. </action>
</release>
<release version="3.0" date="Apr 18 2004">
<action type="fix" dev="HLS" fixes-bug="28345"> Workbench/Upload exception when no file specified </action>
1.11 +112 -10 jakarta-tapestry/framework/src/test/org/apache/tapestry/enhance/TestEnhancementOperation.java
Index: TestEnhancementOperation.java
===================================================================
RCS file: /home/cvs/jakarta-tapestry/framework/src/test/org/apache/tapestry/enhance/TestEnhancementOperation.java,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- TestEnhancementOperation.java 7 Feb 2005 17:31:02 -0000 1.10
+++ TestEnhancementOperation.java 10 Feb 2005 15:47:42 -0000 1.11
@@ -28,11 +28,13 @@
import org.apache.hivemind.service.MethodSignature;
import org.apache.hivemind.service.impl.ClassFactoryImpl;
import org.apache.hivemind.test.HiveMindTestCase;
+import org.apache.tapestry.AbstractComponent;
import org.apache.tapestry.BaseComponent;
import org.apache.tapestry.IComponent;
import org.apache.tapestry.IPage;
import org.apache.tapestry.components.Insert;
import org.apache.tapestry.event.PageDetachListener;
+import org.apache.tapestry.event.PageValidateListener;
import org.apache.tapestry.services.ComponentConstructor;
import org.apache.tapestry.spec.IComponentSpecification;
import org.easymock.MockControl;
@@ -61,6 +63,11 @@
public abstract boolean getFlagProperty();
}
+ public abstract static class ValidatingComponent extends AbstractComponent implements
+ PageValidateListener
+ {
+ }
+
public abstract static class GetClassReferenceFixture
{
public abstract Class getClassReference();
@@ -134,9 +141,14 @@
return (ClassFab) newMock(ClassFab.class);
}
+ private IComponentSpecification newSpec()
+ {
+ return (IComponentSpecification) newMock(IComponentSpecification.class);
+ }
+
public void testConstructorAndAccessors()
{
- IComponentSpecification spec = (IComponentSpecification) newMock(IComponentSpecification.class);
+ IComponentSpecification spec = newSpec();
ClassFactory cf = newClassFactory();
replayControls();
@@ -149,9 +161,99 @@
verifyControls();
}
+ public void testCheckImplementsNoInterface()
+ {
+ IComponentSpecification spec = newSpec();
+ ClassFactory cf = newClassFactory();
+
+ replayControls();
+
+ EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec,
+ BaseComponent.class, cf);
+
+ assertEquals(false, eo.implementsInterface(PageValidateListener.class));
+
+ verifyControls();
+ }
+
+ public void testCheckImplementsClassImplements()
+ {
+ IComponentSpecification spec = newSpec();
+ ClassFactory cf = newClassFactory(ValidatingComponent.class);
+
+ replayControls();
+
+ EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec,
+ ValidatingComponent.class, cf);
+
+ assertEquals(true, eo.implementsInterface(PageValidateListener.class));
+
+ verifyControls();
+ }
+
+ public void testCheckImplementsNoMatchForAddedInterfaces()
+ {
+ IComponentSpecification spec = newSpec();
+
+ MockControl factoryc = newControl(ClassFactory.class);
+ ClassFactory factory = (ClassFactory) factoryc.getMock();
+
+ MockControl classfabc = newControl(ClassFab.class);
+ ClassFab classfab = (ClassFab) classfabc.getMock();
+
+ factory.newClass("$BaseComponent_97", BaseComponent.class);
+ factoryc.setReturnValue(classfab);
+
+ classfab.addInterface(PageDetachListener.class);
+
+ replayControls();
+
+ EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec,
+ BaseComponent.class, factory);
+
+ eo.extendMethodImplementation(
+ PageDetachListener.class,
+ EnhanceUtils.PAGE_DETACHED_SIGNATURE,
+ "foo();");
+
+ assertEquals(false, eo.implementsInterface(PageValidateListener.class));
+
+ verifyControls();
+ }
+
+ public void testCheckImplementsMatchAddedInterfaces()
+ {
+ IComponentSpecification spec = newSpec();
+
+ MockControl factoryc = newControl(ClassFactory.class);
+ ClassFactory factory = (ClassFactory) factoryc.getMock();
+
+ MockControl classfabc = newControl(ClassFab.class);
+ ClassFab classfab = (ClassFab) classfabc.getMock();
+
+ factory.newClass("$BaseComponent_97", BaseComponent.class);
+ factoryc.setReturnValue(classfab);
+
+ classfab.addInterface(PageDetachListener.class);
+
+ replayControls();
+
+ EnhancementOperation eo = new EnhancementOperationImpl(new DefaultClassResolver(), spec,
+ BaseComponent.class, factory);
+
+ eo.extendMethodImplementation(
+ PageDetachListener.class,
+ EnhanceUtils.PAGE_DETACHED_SIGNATURE,
+ "foo();");
+
+ assertEquals(true, eo.implementsInterface(PageDetachListener.class));
+
+ verifyControls();
+ }
+
public void testValidatePropertyNew()
{
- IComponentSpecification spec = (IComponentSpecification) newMock(IComponentSpecification.class);
+ IComponentSpecification spec = newSpec();
ClassFactory cf = newClassFactory();
replayControls();
@@ -168,7 +270,7 @@
public void testValidatePropertyMatches()
{
- IComponentSpecification spec = (IComponentSpecification) newMock(IComponentSpecification.class);
+ IComponentSpecification spec = newSpec();
ClassFactory cf = newClassFactory();
replayControls();
@@ -185,7 +287,7 @@
public void testValidatePropertyMismatch()
{
- IComponentSpecification spec = (IComponentSpecification) newMock(IComponentSpecification.class);
+ IComponentSpecification spec = newSpec();
ClassFactory cf = newClassFactory();
replayControls();
@@ -214,7 +316,7 @@
public void testConvertTypeName()
{
- IComponentSpecification spec = (IComponentSpecification) newMock(IComponentSpecification.class);
+ IComponentSpecification spec = newSpec();
ClassFactory cf = newClassFactory();
replayControls();
@@ -246,7 +348,7 @@
public void testAddField()
{
- IComponentSpecification spec = (IComponentSpecification) newMock(IComponentSpecification.class);
+ IComponentSpecification spec = newSpec();
MockControl cfc = newControl(ClassFactory.class);
ClassFactory cf = (ClassFactory) cfc.getMock();
@@ -274,7 +376,7 @@
Class baseClass = Insert.class;
MethodSignature sig = new MethodSignature(void.class, "frob", null, null);
- IComponentSpecification spec = (IComponentSpecification) newMock(IComponentSpecification.class);
+ IComponentSpecification spec = newSpec();
MockControl cfc = newControl(ClassFactory.class);
ClassFactory cf = (ClassFactory) cfc.getMock();
@@ -303,7 +405,7 @@
public void testGetAccessorMethodName()
{
- IComponentSpecification spec = (IComponentSpecification) newMock(IComponentSpecification.class);
+ IComponentSpecification spec = newSpec();
ClassFactory cf = newClassFactory(Fixture.class);
replayControls();
@@ -406,7 +508,7 @@
public void testGetPropertyType()
{
- IComponentSpecification spec = (IComponentSpecification) newMock(IComponentSpecification.class);
+ IComponentSpecification spec = newSpec();
ClassFactory cf = newClassFactory();
replayControls();
@@ -427,7 +529,7 @@
public void testFindUnclaimedAbstractProperties()
{
ClassResolver cr = (ClassResolver) newMock(ClassResolver.class);
- IComponentSpecification spec = (IComponentSpecification) newMock(IComponentSpecification.class);
+ IComponentSpecification spec = newSpec();
ClassFactory cf = newClassFactory(UnclaimedAbstractPropertiesFixture.class);
replayControls();
1.1 jakarta-tapestry/framework/src/test/org/apache/tapestry/enhance/TestInjectListenerRegistrationWorker.java
Index: TestInjectListenerRegistrationWorker.java
===================================================================
// Copyright 2005 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.enhance;
import org.apache.hivemind.test.HiveMindTestCase;
import org.apache.tapestry.BaseComponent;
import org.apache.tapestry.IComponent;
import org.apache.tapestry.spec.IComponentSpecification;
import org.easymock.MockControl;
/**
* Tests for {@link TestInjectListenerRegistrationWorker}.
*
* @author Howard M. Lewis Ship
* @since 3.1
*/
public class TestInjectListenerRegistrationWorker extends HiveMindTestCase
{
private IComponentSpecification newSpec()
{
return (IComponentSpecification) newMock(IComponentSpecification.class);
}
public void testNonMatch()
{
MockControl control = newControl(EnhancementOperation.class);
EnhancementOperation op = (EnhancementOperation) control.getMock();
IComponentSpecification spec = newSpec();
op.implementsInterface(Runnable.class);
control.setReturnValue(false);
replayControls();
InjectListenerRegistrationWorker w = new InjectListenerRegistrationWorker();
w.setListenerInterface(Runnable.class);
w.performEnhancement(op, spec);
verifyControls();
}
public void testMatch()
{
MockControl control = newControl(EnhancementOperation.class);
EnhancementOperation op = (EnhancementOperation) control.getMock();
IComponentSpecification spec = newSpec();
op.implementsInterface(IComponent.class);
control.setReturnValue(true);
op.extendMethodImplementation(
IComponent.class,
EnhanceUtils.FINISH_LOAD_SIGNATURE,
"getPage().addThisComponentAsListener(this);");
replayControls();
InjectListenerRegistrationWorker w = new InjectListenerRegistrationWorker();
w.setListenerInterface(IComponent.class);
w.setRegisterMethodName("addThisComponentAsListener");
w.performEnhancement(op, spec);
verifyControls();
}
}
1.12 +70 -9 jakarta-tapestry/framework/src/descriptor/META-INF/tapestry.enhance.xml
Index: tapestry.enhance.xml
===================================================================
RCS file: /home/cvs/jakarta-tapestry/framework/src/descriptor/META-INF/tapestry.enhance.xml,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- tapestry.enhance.xml 10 Feb 2005 14:27:37 -0000 1.11
+++ tapestry.enhance.xml 10 Feb 2005 15:47:42 -0000 1.12
@@ -65,15 +65,22 @@
<!-- These can actually run in any order, so we don't specify before or after. -->
- <command id="inject-messages" object="service:InjectMessagesWorker"/>
- <command id="inject-specification" object="service:InjectSpecificationWorker"/>
- <command id="specified-property" object="service:SpecifiedPropertyWorker"/>
- <command id="parameter" object="service:ParameterPropertyWorker"/>
- <command id="inject" object="service:InjectWorker"/>
- <command id="inject-state" object="service:InjectStateWorker"/>
- <command id="inject-component" object="service:InjectComponentWorker"/>
- <command id="inject-bean" object="service:InjectBeanWorker"/>
- <command id="inject-asset" object="service:InjectAssetWorker"/>
+ <command id="inject-messages" object="service:InjectMessagesWorker" before="page-detach-listener"/>
+ <command id="inject-specification" object="service:InjectSpecificationWorker" before="page-detach-listener"/>
+ <command id="specified-property" object="service:SpecifiedPropertyWorker" before="page-detach-listener"/>
+ <command id="parameter" object="service:ParameterPropertyWorker" before="page-detach-listener"/>
+ <command id="inject" object="service:InjectWorker" before="page-detach-listener"/>
+ <command id="inject-state" object="service:InjectStateWorker" before="page-detach-listener"/>
+ <command id="inject-component" object="service:InjectComponentWorker" before="page-detach-listener"/>
+ <command id="inject-bean" object="service:InjectBeanWorker" before="page-detach-listener"/>
+ <command id="inject-asset" object="service:InjectAssetWorker" before="page-detach-listener"/>
+
+ <!-- These need to come last, since the previous commands may have added interfaces
+ to the class. -->
+
+ <command id="page-detach-listener" object="service:InjectPageDetachListenerWorker"/>
+ <command id="page-validate-listener" object="service:InjectPageValidateListenerWorker" after="page-detach-listener"/>
+ <command id="page-render-listener" object="service:InjectPageRenderListenerWorker" after="page-detach-listener"/>
<command id="abstract-property" object="service:AbstractPropertyWorker" after="*"/>
@@ -187,4 +194,58 @@
</invoke-factory>
</service-point>
+ <service-point id="InjectPageDetachListenerWorker" interface="org.apache.tapestry.enhance.EnhancementWorker">
+
+ Registers components as PageDetachListeners, if they implement
+ the interface.
+
+ <invoke-factory>
+ <construct class="org.apache.tapestry.enhance.InjectListenerRegistrationWorker">
+ <set-object property="listenerInterface" value="class:org.apache.tapestry.event.PageDetachListener"/>
+ <set property="registerMethodName" value="addPageDetachListener"/>
+ </construct>
+ </invoke-factory>
+
+ </service-point>
+
+ <service-point id="InjectPageRenderListenerWorker" interface="org.apache.tapestry.enhance.EnhancementWorker">
+
+ Registers components as PageRenderListeners, if they implement
+ the interface.
+
+ <invoke-factory>
+ <construct class="org.apache.tapestry.enhance.InjectListenerRegistrationWorker">
+ <set-object property="listenerInterface" value="class:org.apache.tapestry.event.PageRenderListener"/>
+ <set property="registerMethodName" value="addPageRenderListener"/>
+ </construct>
+ </invoke-factory>
+
+ </service-point>
+
+ <service-point id="InjectPageValidateListenerWorker" interface="org.apache.tapestry.enhance.EnhancementWorker">
+
+ Registers components as PageValidateListeners, if they implement
+ the interface.
+
+ <invoke-factory>
+ <construct class="org.apache.tapestry.enhance.InjectListenerRegistrationWorker">
+ <set-object property="listenerInterface" value="class:org.apache.tapestry.event.PageValidateListener"/>
+ <set property="registerMethodName" value="addPageValidateListener"/>
+ </construct>
+ </invoke-factory>
+
+ </service-point>
+
+ <service-point id="ClassObjectProvider" interface="org.apache.hivemind.service.ObjectProvider">
+
+ Provides the "class:" object prefix. This should be moved to
+ HiveMind proper.
+
+ <create-instance class="org.apache.tapestry.services.impl.ClassObjectProvider"/>
+
+ </service-point>
+
+ <contribution configuration-id="hivemind.ObjectProviders">
+ <provider prefix="class" service-id="ClassObjectProvider"/>
+ </contribution>
</module>
\ No newline at end of file
1.12 +9 -0 jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhancementOperation.java
Index: EnhancementOperation.java
===================================================================
RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhancementOperation.java,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- EnhancementOperation.java 7 Feb 2005 19:41:19 -0000 1.11
+++ EnhancementOperation.java 10 Feb 2005 15:47:42 -0000 1.12
@@ -133,4 +133,13 @@
*/
public void extendMethodImplementation(Class interfaceClass, MethodSignature methodSignature,
String code);
+
+ /**
+ * Returns true if the class implements the specified interface. Checks the base class
+ * (as identified in the specification), but <em>also</em> accounts for any additional
+ * interfaces that may be added by {@link #extendMethodImplementation(Class, MethodSignature, String)}.
+ *
+ */
+
+ public boolean implementsInterface(Class interfaceClass);
}
\ No newline at end of file
1.14 +18 -4 jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhancementOperationImpl.java
Index: EnhancementOperationImpl.java
===================================================================
RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/EnhancementOperationImpl.java,v
retrieving revision 1.13
retrieving revision 1.14
diff -u -r1.13 -r1.14
--- EnhancementOperationImpl.java 7 Feb 2005 17:31:02 -0000 1.13
+++ EnhancementOperationImpl.java 10 Feb 2005 15:47:42 -0000 1.14
@@ -406,16 +406,30 @@
private void addInterfaceIfNeeded(Class interfaceClass)
{
- if (interfaceClass.isAssignableFrom(_baseClass))
- return;
-
- if (_addedInterfaces.contains(interfaceClass))
+ if (implementsInterface(interfaceClass))
return;
_classFab.addInterface(interfaceClass);
_addedInterfaces.add(interfaceClass);
}
+ public boolean implementsInterface(Class interfaceClass)
+ {
+ if (interfaceClass.isAssignableFrom(_baseClass))
+ return true;
+
+ Iterator i = _addedInterfaces.iterator();
+ while (i.hasNext())
+ {
+ Class addedInterface = (Class) i.next();
+
+ if (interfaceClass.isAssignableFrom(addedInterface))
+ return true;
+ }
+
+ return false;
+ }
+
private BodyBuilder createIncompleteMethod(MethodSignature sig)
{
BodyBuilder result = new BodyBuilder();
1.1 jakarta-tapestry/framework/src/java/org/apache/tapestry/enhance/InjectListenerRegistrationWorker.java
Index: InjectListenerRegistrationWorker.java
===================================================================
// Copyright 2005 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.enhance;
import org.apache.tapestry.IComponent;
import org.apache.tapestry.spec.IComponentSpecification;
/**
* Checks to see if the component implements a specific interface, and adds code to the component's
* finishLoad() method to register (with the page) for such notifications.
*
* @author Howard M. Lewis Ship
* @since 3.1
*/
public class InjectListenerRegistrationWorker implements EnhancementWorker
{
private Class _listenerInterface;
private String _registerMethodName;
public void performEnhancement(EnhancementOperation op, IComponentSpecification spec)
{
if (op.implementsInterface(_listenerInterface))
{
op.extendMethodImplementation(
IComponent.class,
EnhanceUtils.FINISH_LOAD_SIGNATURE,
"getPage()." + _registerMethodName + "(this);");
}
}
public void setListenerInterface(Class listenerInterface)
{
_listenerInterface = listenerInterface;
}
public void setRegisterMethodName(String registerMethodName)
{
_registerMethodName = registerMethodName;
}
}
1.15 +0 -16 jakarta-tapestry/framework/src/java/org/apache/tapestry/AbstractComponent.java
Index: AbstractComponent.java
===================================================================
RCS file: /home/cvs/jakarta-tapestry/framework/src/java/org/apache/tapestry/AbstractComponent.java,v
retrieving revision 1.14
retrieving revision 1.15
diff -u -r1.14 -r1.15
--- AbstractComponent.java 10 Feb 2005 14:27:37 -0000 1.14
+++ AbstractComponent.java 10 Feb 2005 15:47:42 -0000 1.15
@@ -28,10 +28,7 @@
import org.apache.hivemind.util.PropertyUtils;
import org.apache.tapestry.bean.BeanProvider;
import org.apache.tapestry.engine.IPageLoader;
-import org.apache.tapestry.event.PageDetachListener;
import org.apache.tapestry.event.PageEvent;
-import org.apache.tapestry.event.PageRenderListener;
-import org.apache.tapestry.event.PageValidateListener;
import org.apache.tapestry.listener.ListenerMap;
import org.apache.tapestry.spec.IComponentSpecification;
@@ -213,10 +210,6 @@
}
/**
- * Registers this component as a listener of the page if it implements
- * {@link org.apache.tapestry.event.PageDetachListener},
- * {@link org.apache.tapestry.event.PageRenderListener}or {@link PageValidateListener}.
- * <p>
* Invokes {@link #finishLoad()}. Subclasses may overide as needed, but must invoke this
* implementation. {@link BaseComponent}loads its HTML template.
*/
@@ -224,15 +217,6 @@
public void finishLoad(IRequestCycle cycle, IPageLoader loader,
IComponentSpecification specification)
{
- if (this instanceof PageDetachListener)
- _page.addPageDetachListener((PageDetachListener) this);
-
- if (this instanceof PageRenderListener)
- _page.addPageRenderListener((PageRenderListener) this);
-
- if (this instanceof PageValidateListener)
- _page.addPageValidateListener((PageValidateListener) this);
-
finishLoad();
}
---------------------------------------------------------------------
To unsubscribe, e-mail: tapestry-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: tapestry-dev-help@jakarta.apache.org