Although his example describes how to create a sandbox and production version of a service using symbols, this method can be used for various other scenarios.

Scenario: you are developing a shopping cart and want to test your app against the sandbox PayPal NVPServer, without having to hard-code the variables into your implementation:

Note: This examples assumes you can read symbols from properties files (see Tapestry5HowToReadSymbolsFromPropertiesFile). Also, it uses the PayPal NVP library.

public class AppModule {
    // make configuration from '' on the classpath available as symbols
    public PropertiesFileSymbolProvider buildPayPalPropertiesFileSymbolProvider(Logger logger) {
        return new PropertiesFileSymbolProvider(logger, "", true);
    public static void contributeSymbolSource(OrderedConfiguration<SymbolProvider> configuration,
        @InjectService("PayPalPropertiesFileSymbolProvider") SymbolProvider payPalPropertiesFileSymbolProvider) {
        configuration.add("PayPalPropertiesFile", payPalPropertiesFileSymbolProvider, "after:SystemProperties", "before:ApplicationDefaults");

we have a properties file that stores our credentials for the different services (src/main/resources/


Our PayPalNVPService Implementation looks something like this:

public class PayPalNVPService {
    /* set user - these are your credentials from paypal */
    private Profile user;
    private Environment environment;
    public PayPalNVPService(Environment environment, String apiUsername, String apiPassword, String signature) {        
        this.environment = environment;
        user = new BaseProfile.Builder(apiUsername, apiPassword).signature(signature).build();
    public boolean SetExpressCheckout(String returnURL, String cancelURL, ... ) {

        /* create new instance of paypal nvp */
        PayPal pp = new PayPal(user, environment);

Ideally we want to be able to do something like @Inject @Symbol("paypal.api.${paypal.api.endpoint}.username") String userName. However this doesn't work, as only @Value and @InjectService allow Symbol substitution. Thus, we will have to to create a new symbol:

public class AppModule {
    public static void contributeApplicationDefaults(MappedConfiguration<String, String> configuration) {
        configuration.add("PayPalNVPService", "paypal.nvp.${paypal.api.endpoint}");

And now we'll just need to create two different services, based on the different credentials:

public class AppModule {
    public static PayPalNVPService buildPayPalNVPSandboxService(
        @Inject @Symbol(value="paypal.api.sandbox.username") String apiUsername,
        @Inject @Symbol(value="paypal.api.sandbox.password") String apiPassword,
        @Inject @Symbol(value="paypal.api.sandbox.signature") String signature) {
            return new PayPalNVPService(Environment.SANDBOX, apiUsername, apiPassword, signature);

    public static PayPalNVPService buildPayPalNVPLiveService(
        @Inject @Symbol(value="") String apiUsername,
        @Inject @Symbol(value="") String apiPassword,
        @Inject @Symbol(value="") String signature) {
            return new PayPalNVPService(Environment.LIVE, apiUsername, apiPassword, signature);

(If anyone knows how to evaluate symbols inside contributeApplicationDefaults, please let me know as I would prefer to just simply create 3 new symbols for username, password and signature and then in inject the symbols into the service implementation and used auto-building.)

And lastly inject the service into our page. Notice how we have to evaluate the PayPalNVPService symbol to get the actual service id:

public class ViewCart {

    private PayPalNVPService paypalService;

    public void onActionFromCheckoutWithPayPal() {
        boolean success = paypalService.SetExpressCheckout("http://localhost:8080/website/checkout/revieworder", "http://localhost:8080/website/viewcart", ...);

Tapestry5HowToCreateAConfigureableService (last edited 2010-07-09 12:46:26 by DanielCaldeweyher)