...
Excerpt |
---|
|
How to create a library of your custom components |
Warning |
---|
This page has not yet been fully updated for Tapestry 5.4. Things are different and simpler in 5.4 than in previous releases. |
Creating Component Libraries
Nearly every Tapestry application includes a least a couple of custom components, specific to the application. What's exciting about Tapestry is how easy it is to package components for reuse across many applications ... and the fact that applications using a component library need no special configuration.
Div |
---|
style | float:right |
---|
title | Related Articles |
---|
class | aui-label |
---|
|
Content by Label |
---|
showLabels | false |
---|
showSpace | false |
---|
title | Related Articles |
---|
cql | label = "components" and space = currentSpace() |
---|
|
|
A Tapestry component library consists of components (and optionally mixins, pages and component base classes). In addition, a component library will have a module that can define new services (needed by the components) or configure other services present in Tapestry. Finally, components can be packaged with assets: resources such as images, stylesheets and JavaScript libraries that need to be provided to the client web browser.
...
Tapestry doesn't mandate that you use any build system, but we'll assume for the moment that you are using Maven 2. In that case, you'll have a pom.xml file something like the following:
Code Block |
---|
XMLlanguage | XMLxml | title | pom.xml |
---|
|
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>happylib</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>happylib Tapestry 5 Library</name>
<dependencies>
<dependency>
<groupId>org.apache.tapestry</groupId>
<artifactId>tapestry-core</artifactId>
<version>${tapestry-release-version}</version>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>5.1</version>
<classifier>jdk15</classifier>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
<optimize>true</optimize>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Tapestry-Module-Classes>org.example.happylib.services.HappyModule</Tapestry-Module-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>codehaus.snapshots</id>
<url>http://snapshots.repository.codehaus.org</url>
</repository>
<repository>
<id>OpenQA_Release</id>
<name>OpenQA Release Repository</name>
<url>http://archiva.openqa.org/repository/releases/</url>
</repository>
</repositories>
<properties>
<tapestry-release-version>5.2.0<4-beta-28</tapestry-release-version>
</properties>
</project>
|
...
Our component is very simple:
Code Block |
---|
javalanguage | java | title | HappyIcon.java |
---|
|
package org.example.happylib.components;
import org.apache.tapestry5.Asset;
import org.apache.tapestry5.MarkupWriter;
import org.apache.tapestry5.annotations.Path;
import org.apache.tapestry5.ioc.annotations.Inject;
public class HappyIcon
{
@Inject
@Path("happy.jpg")
private Asset happyIcon;
boolean beginRender(MarkupWriter writer)
{
writer.element("img", "src", happyIcon);
writer.end();
return false;
}
}
|
...
The above naming is somewhat clumsy, and can be improved by introducing an additional namespace into the template:
Code Block |
---|
|
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd"
xmlns:h="tapestry-library:happy">
...
<h:icon/>
...
</html>
|
...
At application startup, Tapestry will read the library module along with all other modules and configure the ComponentClassResolver service using information in the module:
Code Block |
---|
javalanguage | java | title | HappyModule.java |
---|
|
package org.example.happylib.services;
import org.apache.tapestry5.ioc.Configuration;
import org.apache.tapestry5.services.LibraryMapping;
public class HappyModule
{
public static void contributeComponentClassResolver(Configuration<LibraryMapping> configuration)
{
configuration.add(new LibraryMapping("happy", "org.example.happylib"));
}
}
|
...
For Tapestry to load your module at application startup, it is necessary to put an entry in the JAR manifest. This is taken care of in the pom.xml above:
Code Block |
---|
xmllanguage | xml | title | pom.xml (partial) |
---|
|
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Tapestry-Module-Classes>org.example.happylib.services.HappyModule</Tapestry-Module-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
|
...
As of Tapestry 5.2, a new step is needed: extending access for the assets. This is accomplished in your library's module class, HappyModule:
Code Block |
---|
|
public static void contributeRegexAuthorizer(Configuration<String> configuration)
{
configuration.add("^org/example/happylib/.*\\.jpg$");
}
|
...
To handle this problem in Tapestry 5.1 and earlier, you should map your library assets to a versioned folder. This can be accomplished using another contribution from the HappyModule, this time to the ClasspathAssetAliasManager service whose configuration maps a virtual folder underneath /assets to a package:
Code Block |
---|
|
public static void contributeClasspathAssetAliasManager(MappedConfiguration<String, String> configuration)
{
configuration.add("happylib/1.0", "org/example/happylib");
}
|
With this in place, and the library and applications rebuilt and redeployed, the URL for happy.jpg becomes /happyapp/assets/happylib/1.0/components/happy.jpg. This is shorter, but also incorporates a version number ("1.0") that can be changed in a later release.
Since |
---|
| In version | and later, Tapestry automatically creates a mapping for assets inside your JAR. In the above example, the icon image will be exposed as /assets/
application version/happy/components/happy.jpg
(the application version number is incorporated into the URL). The "happy" portion is a virtual folder that maps to the library's root package (as folder org/example/happylib
on the Java classpath). The application version is a configurable value. |
|
Conclusion
That's it! Autoloading plus the virtual folders for components and for assets takes care of all the issues related to components. Just build your JARs, setup the JAR Manifest, and drop them into your applications.