Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Tapestry Wiki" for 
change notification.

The following page has been changed by UlrichStaerk:
http://wiki.apache.org/tapestry/Tapestry5HowToReadSymbolsFromPropertiesFile

New page:
[[TableOfContents]]
= Motivation =
Tapestry already supports configuration symbols using system properties (e.g. 
-Dkey=value) on the command line and one can contribute to ApplicationDefaults 
or FactoryDefaults in one's AppModule or you could provide configuration 
symbols as init parameters in your web.xml. But I neither wanted to supply all 
my configuration on the command line nor did I want to hard-code it into my 
application nor did I want to have a web.xml bloated with configuration. In 
these cases I'd have to restart the web container or redeploy my application in 
order for configuration changes to take effect. A properties file would be much 
nicer as I could change that, reload the context and have my configuration 
changes in effect.

= Background =
In Tapestry, configuration symbols can be accessed in services, pages and 
components through SymbolProviders which are contributed to the SymbolSource 
service. If you look up a symbol, the SymbolSource service queries an ordered 
list of SymbolProviders for that symbol. The first provider takes precedence 
over the following SymbolProviders, so it is possible to overwrite 
configuration symbols in some other place. For example the built-in 
ApplicationDefaults SymbolProvider takes precedence over FactoryDefaults so 
that you can contribute to the ApplicationDefaults service and thus can 
overwrite values set in FactoryDefaults. Similarily system properties, i.e. 
properties supplied on the command line with the -Dkey=value syntax, overwrite 
both ApplicationDefaults and FactoryDefaults.

Tapestry ships with 4 SymbolProviders of which 2 are in use by default:

 * SingleKeySmbolProvider takes 2 constructor arguments (a key and a value 
String) and maps a single key to a value
 * ServletContextSymbolProvider makes init parameters in your web.xml 
accessible as symbols
 * MapSymbolProvider internally stores key-value pairs in a map. 
FactoryDefaults and ApplicationDefaults are MapSymbolProviders.
 * SystemPropertiesSymbolProvider makes system properties (coming from your 
JVM) available as symbols

= Implementation =
I wanted to have a SymbolProvider that looks for configuration symbols in a 
properties file either located somewhere on the filesystem or in the classpath. 
I therefore wrote a PropertiesFileSymbolProvider that implements SymbolProvider:

{{{
public class PropertiesFileSymbolProvider implements SymbolProvider
{
    private Properties properties;

    public PropertiesFileSymbolProvider(Logger logger, String resourceName, 
boolean classPath)
    {
        properties = new Properties();
        
        try
        {
            InputStream in;
            
            if (classPath)
                in = ClassLoader.getSystemResourceAsStream(resourceName);
            else
                in = new FileInputStream(resourceName);

            // ClassLoader.getSystemResourceAsStream() returns null if
            // the resource cannot be found on the classpath
            if (in == null)
                throw new FileNotFoundException();
            
            properties.load(in);

        } catch (FileNotFoundException e)
        {
            logger.error("Could not find '" + resourceName + "'");
        } catch (IOException e)
        {
            logger.error("IOException while loading '" + resourceName + "': " + 
e.getMessage());
        }
    }
    
    public String valueForSymbol(String arg0)
    {
        return properties.getProperty(arg0);
    }
}
}}}

= Tying it together =

Next you have to define one (or more) instances of PropertiesFileSymbolProvider 
as a service in your AppModule. In the following example I'm defining two 
services: ClasspathPropertiesFileSymbolProvider looks for the file 
test.properties in the classpath and FilesystemPropertiesFileSymbolProvider 
looks for the file test2.properties somewhere in the file system. The 
constructor for the PropertiesFileSymbolProvider takes 3 arguments: the first 
is a Logger object which is provided by Tapestry. The second is the name of the 
resource to look for and the third indicates whether to look in the classpath 
(true) or on the file system (false).

{{{
// make configuration from 'test.properties' on the classpath available as 
symbols
public PropertiesFileSymbolProvider 
buildClasspathPropertiesFileSymbolProvider(Logger logger)
{
    return new PropertiesFileSymbolProvider(logger, "test.properties", true);
}

// make configuration from 'test2.properties' on the filesystem available as 
symbols
public PropertiesFileSymbolProvider 
buildFilesystemPropertiesFileSymbolProvider(Logger logger)
{
    return new PropertiesFileSymbolProvider(logger, 
"src/main/webapp/WEB-INF/test2.properties", false);
}
}}}

The last step is to contribute those two services to the SymbolSource service 
so that we can access our configuration symbols from our services, pages and 
components.

{{{
public static void contributeSymbolSource(OrderedConfiguration<SymbolProvider> 
configuration,

        @InjectService("ClasspathPropertiesFileSymbolProvider")

        SymbolProvider classpathPropertiesFileSymbolProvider,

        @InjectService("FilesystemPropertiesFileSymbolProvider")

        SymbolProvider filesystemPropertiesFileSymbolProvider)
{
    configuration.add("ClasspathPropertiesFile", 
classpathPropertiesFileSymbolProvider, "after:SystemProperties", 
"before:ApplicationDefaults");

    configuration.add("FilesystemPropertiesFile", 
filesystemPropertiesFileSymbolProvider, "after:ClasspathPropertiesFile", 
"before:ApplicationDefaults");
}
}}}

The order of your SymbolProviders is important (before: and after: syntax). The 
SymbolProviders for the default SymbolSource service are ordered 
SystemProperties, ApplicationDefaults, FactoryDefaults. Thus system properties 
override contributions to the ApplicationDefaults service which in turn 
override contributions to the FactoryDefaults service. If you don't want your 
configuration symbols being overriden by any of the pre-defined SymbolProviders 
you have to make sure to insert them in the correct place. In my case I wanted 
the properties from the file on the classpath to be overridable only by system 
properties and the properties from the file on the filesystem be overridable by 
system properties and the file on the classpath. Thus my order is 
SystemProperties, ClasspathPropertiesFile, FilesystemPropertiesFile, 
ApplicationDefaults, FactoryDefaults.

= Using symbols in pages, components and services =

To use configuration symbols in your pages and components you just have to 
inject them. A simple symbol value can be injected with:

{{{
@Inject @Symbol(value="foo")
private String foo;
}}}

The foo string will then contain the value of the foo configuration symbol. If 
for example your properties file contained a line `foo=bar`, then the foo 
string would be "bar". If you want to expand a symbol in a string, you have to 
use the Value annotation:

{{{
@Inject @Value(value="${root}/something/${somedir})
private String directory
}}}

If you had a properties file whith the lines `root=/var/lib` and `somedir=foo` 
than the directory string would contain "/var/lib/something/foo". Tapestry 
tries to find a symbol whose name matches the content inside `${}` and expands 
the string whith that symbol's value.

For information on how to use symbols inside your services, please refer to 
http://tapestry.apache.org/tapestry5/tapestry-ioc/symbols.html.

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to