Integration and Exports

AndHow can integrate with other frameworks and legacy apps.

Strong typing, validation at startup, and semantic property names are nice features for new applications, but what about existing applications that are relying on finding specifically named properties?

Manual Export to Maps, java.util.Properties and more

AndHow can export groups of AndHow Properties to Maps, java.util.Properties, or any structure you need. Here is an example:

public class AppDataAccess {

	@ManualExportAllowed(
		useCanonicalName=Exporter.EXPORT_CANONICAL_NAME.NEVER)
	private interface HibernateConfig {
		StrProp USER = StrProp.builder()
			.aliasInAndOut("hibernate.connection.username")
			.notNull().build();
		StrProp PWD = StrProp.builder()
			.aliasInAndOut("hibernate.connection.password")
			.notNull().build();
		IntProp POOL_SIZE = IntProp.builder()
			.aliasInAndOut("hibernate.connection.pool_size")
			.defaultValue(20).lessThan(200).build();
	}

	public Properties getHibernateProperties() throws IllegalAccessException {
		return AndHow.instance().export(HibernateConfig.class)
			.collect(ExportCollector.stringProperties(""));
	}
}

This example supposes were are configuring the Hibernate framework (an ORM tool that connects to a database). Hibernate accepts a java.util.Properties for configuration, so we can use AndHow to export Properties for it. The resulting java.utils.Property would contain values like this:

  • hibernate.connection.username = [The configured username]

  • hibernate.connection.password = [The configured password]

  • hibernate.connection.pool_size = [The configured pool size]

Some key points of exporting:

  • @ManualExportAllowed is required to allow exports. It applies to all AndHow Properties contained in the class or interface it is on, and is inherited by any nested inner classes or interfaces.

  • AndHow.instance().export(AppDataAccess.class) will stream() PropertyExport instances, one per Property contained in the exported class. Properties contained directly or in nested innerclasses/interfaces will be included.

  • A PropertyExport object contains a reference to the Property being exported, its value, and other metadata relevant for exporting. Inspecting and modifying the PropertyExport in the stream gives fine grained control over the export.

  • Using Property.aliasInAndOut or aliasOut gives easy control over the name used to export. In this example, the alias name is intended to match the property names Hibernate is expecting. The options on @ManualExportAllowed also control which names are exported. By default, 'out' names are always used and in this example the cannonical names are turned off. Its possible to have multiple names exported for each Property.

Adding the @ManualExportAllowed widens the visibility of the contained Properties. Normally a private Property is not visible outside the class that declares it. However, if the class allows export, any class with a reference to the containing class can export and read the values of the contained Properties.

In the example, if @ManualExportAllowed had been placed on AppDataAccess, the result would be the same, however, any private Properties contained in AppDataAccess would indirectly expose their value to any class with a reference to AppDataAccess.

To enable large groups of innerclasses/interfaces to be exported, @ManualExportAllowed can be placed on the containing class. To block some select innerclasses, @ManualExportNotAllowed can be used.

//TODO: There are lots of varieties of exports to include. The AndHow.export() method javadocs contains lots of examples.

Auto-Export to System Properties

AndHow can export property names and values to bridge the gap between legacy code and AndHow Properties. Below is an example that could be used for a legacy code expecting to find configuration in System.Properties.

import org.yarnandtail.andhow.*;
import static org.yarnandtail.andhow.api.Exporter.*;
import org.yarnandtail.andhow.export.SysPropExporter;

@GroupExport(
    exporter=SysPropExporter.class,
    exportByCanonicalName=EXPORT_CANONICAL_NAME.NEVER,
    exportByOutAliases=EXPORT_OUT_ALIASES.ALWAYS
)
public interface ShoppingCartSvsConfig {
    StrProp SERVICE_URL = StrProp.builder()
        .mustEndWith("/").aliasInAndOut("cart.svs").build();
    IntProp TIMEOUT = IntProp.builder()
        .aliasInAndOut("cart.to").aliasOut("timeout").build();
}

The annotation in the example above specifies that as soon as the startup value loading is completed:

  • All the Properties contained directly in the class will be exported as a name-value pair to System.Properties at startup.

  • The property name used will be the 'out' alias, if the property has one, otherwise the canonical name of the Property is used

  • The value will be the validated value loaded by AndHow

Take care to ensure that AndHow is initialized before legacy code attempts to read exported valeus. This can be done by simply calling AndHow.instance(); at your code entry point, or reading the value of any AndHow Property.

Also note that only properties contained directly in the annotated class are exported: Properties in nested inner classes or interfaces are not included, though those inner classes could also be annotated.

In this exmple, each property is given an InAndOut alias matching the name that the legacy code is expectiving to find in the System.Properties. The 'out' portion of that specifies a name available for export. The 'in' portion is an added name that will be recognized when reading property values from a configuration source, like JNDI or a properties file. Aliases can be specified as 'in' or 'out' only as well if needed to avoid name collisions.

By using GroupExport and aliasInAndOut, a legacy application can be virtually unchanged and still benefit from strong typing, validation checks, multi-source loading and other features of AndHow.

Last updated