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 'paypal.properties' on the classpath available as symbols
    public PropertiesFileSymbolProvider buildPayPalPropertiesFileSymbolProvider(Logger logger) {
        return new PropertiesFileSymbolProvider(logger, "paypal.properties", 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/paypal.properties):

#paypal.api.endpoint=live
paypal.api.endpoint=sandbox

paypal.api.sandbox.username=my_sandbox_username@email.com
paypal.api.sandbox.password=my_sandbox_password@email.com
paypal.api.sandbox.signature=SANDBOX_SIGNATURE

paypal.api.live.username=my_live_username@email.com
paypal.api.live.password=my_live_username@email.com
paypal.api.live.signature=LIVE_SIGNATURE

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 {
    @ServiceId("paypal.nvp.sandbox")
    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);
    }

    @ServiceId("paypal.nvp.live")
    public static PayPalNVPService buildPayPalNVPLiveService(
        @Inject @Symbol(value="paypal.api.live.username") String apiUsername,
        @Inject @Symbol(value="paypal.api.live.password") String apiPassword,
        @Inject @Symbol(value="paypal.api.live.signature") 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 {

    @InjectService("${PayPalNVPService}")
    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)