Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: restore

In Tapestry, Assets are any kind of static content that may be downloaded to a client web browser, such as images, style sheets and JavaScript files.

Div
stylefloat:right
titleRelated Articles
classaui-label
Content by Label
showLabelsfalse
showSpacefalse
titleRelated Articles
cqllabel in ("assets","response") and space = currentSpace()

Assets can be in one of three places within a Tapestry app:

...

For referencing assets from templates, two binding prefixes exist: "context:" and "asset:". The "context:" prefix matches assets in the web application's context folder, and the "asset:" prefix is for assets from the classpath.

Code Block
languagexml
titlesrc/main/webapp/com/example/myapp/images/tapestry_banner.gif
<img src="${context:images/tapestry_banner.gif}"/>
Info

This is an example of using a template expansion inside an ordinary element (rather than a component).

If you don't provide either prefix, "asset:" is assumed.

...

Components access assets via injection, using the @Inject annotation, which allows Assets to be injected into components as read-only properties. The path to the resource is specified using the Path annotation:

Code Block
languagejava
@Inject
@Path("context:images/tapestry_banner.gif")
private Asset banner;

Assets are located within domains; these domains are identified by the prefix on the @Path annotation's value.

...

You can use relative paths with domains (if you omit the prefix):

Code Block
languagejava
@Inject
@Path("images/edit.png")
private Asset icon;

This represents a relative path from the default location for the asset. For Tapestry 5.4, this will resolve as either relative to the component's class file (the logic for Tapestry 5.3 and earlier), or relative to the correct folder within META-INF/assets (the logic for Tapestry 5.4 and later).

...

Symbols inside the annotation value are expanded. This allows you to define a symbol and reference it as part of the path. For example, you could contribute a symbol named "skin.root" as "context:skins/basic" and then reference an asset from within it:

Code Block
languagejava
@Inject
@Path("${skin.root}/style.css")
private Asset style;
Note

The use of the ${...} syntax here is a symbol expansion (because it occurs in an annotation in Java code), rather than a template expansion (which occurs only in Tapestry template files).

An override of the skin.root symbol would affect all references to the named asset.

Localization of Assets

Main Article: Localization Assets

Assets are localized; Tapestry will search for a variation of the file appropriate to the effective locale for the request. In the previous example, a German user of the application may see a file named edit_de.png (if such a file exists).

...

  • version: Application version number, defined by the tapestry.application-version symbol in your application module (normally AppModule.java). The default is a random hex number.
  • folder: Identifies the library containing the asset, or "ctx" for a context asset, or "stack" (used when combining multiple JavaScript files into a single virtual asset).
  • path: The path below the root package of the library to the specific asset file.

Asset Fingerprinting (Tapestry 5.4 and later)

Tapestry 5.4 changes how Asset URLs are constructed. The version number is now a content fingerprint, a hash of the actual content of the asset.

...

Tapestry GZIP compresses the content of all assets – if the asset is compressible, the client supports it, and you don't explicitly disable it.

Further, the asset will get a far future expires header, which will encourage the client browser to cache the asset.

You should have an explicit application version number for any production application. Client browsers will aggressively cache downloaded assets; they will usually not even send a request to see if the asset has changed once the asset is downloaded the first time. Because of this it is very important that each new deployment of your application has a new version number, to force existing clients to re-download all assets.

Asset Security

Warning

This applies to how Tapestry 5.3 and earlier manage classpath assets; Tapestry 5.4 introduces another system which doesn't have this issue.

Because Tapestry directly exposes files on the classpath to the clients, some thought has gone into ensuring that malicious clients are not able to download assets that should not be visible to them.

...

  • Your Login page exposes a classpath asset, icon.png.
  • A malicious client copies the URL, /assets/1.0.0/app/pages/icon.png (which would indicate that the Login page is actually inside a library, which is unlikely. More likely, icon.png is a context asset and the malicious user guessed the path for Login.class by looking at the Tapestry source code.) and changes the file name to Login.class.

  • The client decompiles the class file and spots your secret emergency password: goodbye security! (Never create such back doors, of course!)

Fortunately, this can't happen. Files with extension ".class" are secured; they must be accompanied in the URL with a query parameter that is the MD5 hash of the file's contents. If the query parameter is absent, or doesn't match the actual file's content, the request is rejected.

When your code exposes an Asset that is secured, Tapestry generates a URL that automatically includes MD5 hash query parameter. The malicious user is locked out of access to the files. (The only way they could generate the MD5 hash is if they somehow already have the files, in which case they don't need to download them again anyway.)

By default, Tapestry secures file extensions ".class', ".tml" and ".properties". The list can be extended by contributing to the ResourceDigestGenerator service:

Code Block
languagejava
titleAppModule.java (partial)
public static void contributeResourceDigestGenerator(Configuration<String> configuration)
{
    configuration.add("xyz");
}
Warning

Starting in Tapestry 5.4, there is a move to ensure that all assets are stored under META-INF/assets, rather than on the general classpath.

In Tapestry 5.5 and later, assets on the general classpath may not be supported at all.

Minimizing Assets

Since version 5.3, Tapestry provides a service, ResourceMinimizer, which will help to minimize all your static resources (principally CSS and JavaScript files).

...

By default, this service does nothing. You should include a the tapestry-yuicompressor library (for Tapestry 5.3) or tapestry-webresources (for Tapestry 5.4), which makes it possible to minimize CSS and JavaScript files.

Section
Column
Code Block
languagexml
titleFor Tapestry 5.3: pom.xml (partial)
<dependency>
    <groupId>org.apache.tapestry</groupId>
    <artifactId>tapestry-yuicompressor</artifactId>
    <version>5.3.1</version>
</dependency>

Column
Code Block
languagexml
langxml
titleFor Tapestry 5.4: pom.xml (partial)
<dependency>
    <groupId>org.apache.tapestry</groupId>
    <artifactId>tapestry-webresources</artifactId>
    <version>5.4</version>
</dependency>

 

By adding this dependency, all your JavaScript and CSS files will be minimized when PRODUCTION_MODE=true. You can force the minimization of these files, by changing the value of the constant SymbolConstants.MINIFICATION_ENABLED in your module class (usually AppModule.java):

Code Block
languagejava
titleAppModule.java (partial)
@Contribute(SymbolProvider.class)
@ApplicationDefaults
public static void contributeApplicationDefaults(MappedConfiguration<String, String> configuration)
{
    configuration.add(SymbolConstants.MINIFICATION_ENABLED, "true");
}

If you want to add your own minimizer for particular types of assets, you can contribute to the ResourceMinimizer service. The service configuration maps the MIME-TYPE of your resource to an implementation of the ResourceMinimizer interface.

Code Block
languagejava
titleAppModule.java (partial)
@Contribute(ResourceMinimizer.class)
@Primary
public static void contributeMinimizers(MappedConfiguration<String, ResourceMinimizer> configuration)
{
    configuration.addInstance("text/coffeescript", CoffeeScriptMinimizer.class);
}