7.2. System Repository

Provides a function to manage objects used in various places and configuration values when implementing an application.

The function can be used for the following.

  • Logic that may differ for each environment (generated class and property values) can be defined in an external file.
  • Relationships can be built between objects based on the definition of external files. (with DI container function)

7.2.1. Function overview

7.2.1.1. Objects can be constructed using DI containers

The DI container feature allows objects to be constructed based on the definition of components defined in xml or the annotated class. The constructed object is a singleton.

The DI container function can do the following:

DI container is accessed from the system repository instead of direct access from the application. For details, see Configure the DI container information to the system repository.

7.2.1.2. Objects can be initialized

Any initialization processing can be executed after object construction.

Since restrictions may occur in the initialization order based on the dependency of the objects, this function allows the initialization order of the objects to be specified.

For details, see Initialize the object.

7.2.2. Module list

<dependency>
  <groupId>com.nablarch.framework</groupId>
  <artifactId>nablarch-core</artifactId>
</dependency>
<dependency>
  <groupId>com.nablarch.framework</groupId>
  <artifactId>nablarch-core-repository</artifactId>
</dependency>

7.2.3. How to use

7.2.3.1. Define root node in xml

The root node of the component configuration file (xml) is component-configuration. If schemaLocation is configured correctly, the document of each element and attribute in the IDE can be referred, and the completion functions can be utilized effectively.

<component-configuration xmlns="http://tis.co.jp/nablarch/component-configuration"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://tis.co.jp/nablarch/component-configuration /component-configuration.xsd">

</component-configuration>

Refer below for details on how to define components in xml.

7.2.3.2. Configure Java Beans object

Java Beans object is defined using the component element.

  • Configure FQCN of the class managed by DI container in class attribute.
  • An any name can be configured using the name attribute.
  • Setter can be injected using the property child element.
  • Component can be defined in the property child element.
  • Component defined elsewhere can be injected with the setter using ref attribute of property.

An example is shown below.

<!-- Configure Java Beans object using component element -->
<component name="sample" class="sample.SampleBean" />

<component name="component" class="sample.SampleComponent">
  <!--
   Setter injection with property element
   In this example, the object defined as component with the name sample is injected
   -->
  <property name="sample" ref="sample" />

  <!-- Component can also be defined in the child element of property
  without using the ref attribute -->
  <property name="obj">
    <component class="sample.SampleObject" />
  </property>

  <!-- Setter injection of literal value -->
  <property name="limit" value="100" />
<component/>

Important

The created instance is a singleton. Therefore, note the following points.

  • Since the instance is a singleton, it is not created each time it is acquired (not a prototype).
  • The instance is not destroyed until the application is terminated.

Special caution is required as a serious bug will be embedded if this is not understood. For example, there is a possibility of causing serious bugs such as the generated instance mistaken for a prototype, and a certain request will cause the input value of user A to be set in the component and request from another user B will use that value.

If the state of a component is intentionally changed or shared across the application, that component must be thread-safe.

Tip

Instance of an object is created for each component element. For example, if a component is defined in 2 places as follows, separate instances will be created.

<!-- Two instances of SampleBean are registered in the repository -->
<component name="sample1" class="sample.SampleBean" />
<component name="sample2" class="sample.SampleBean" />

Tip

Since the component defined by nesting is also stored in the global area of the repository, the name can be specified to acquire the object. For information on how to get the object, see Get object from system repository.

Tip

Injection is not performed for static properties (static setter methods). If the property to be injected is static, an exception will be thrown when building the DI container.

7.2.3.3. Overwrite the configuration of Java Beans object

The configuration of previously read objects can be overwritten by registering objects with the same name attribute of component tag. This function can be used to replace the object for production environment with the object (mock) for testing when testing is conducted.

When an object is overwritten, simply registering an object with the same name will automatically give priority to the object that is read later.

An example is shown below.

<component name="sample" class="sample.SampleBean">
  <property name="prop" value="message" />
</component>

<!-- Define and overwrite a component with the same name -->
<component name="sample" class="sample.MockSampleBean" />

Important

If different classes are configured as in the above example, all the property configurations before overwriting will be discarded. This is because even if the class implements the same interface, they do not always have the same property.

However, when the same class is configured, the configuration of property before overwriting are all inherited to the class after overwriting. Therefore, the configuration to a specific property cannot be removed with the configuration after overwriting. For example, when the following overwrite configuration is configured, the property element does not exist in the configuration after overwriting, but message is configured in prop as the value of prop before overwriting is inherited.

<component name="sample" class="sample.SampleBean">
  <property name="prop" value="message" />
</component>

<!--
Property is not set, but the value of prop before overwriting is inherited
 -->
<component name="sample" class="sample.SampleBean" />

7.2.3.4. Use a string, numeric, or boolean value as the configuration value

If property type is of the following type, the value can be easily configured using literal notation.

  • java.lang.String
  • java.lang.String[]
  • java.lang.Integer(int)
  • java.lang.Integer[](int[])
  • java.lang.Long(long)
  • java.lang.Boolean(boolean)

A configuration example is shown below.

java.lang.String

When configuring a value in java.lang.String type, describe the value to be configured with literal in the value attribute.

In this example, “abcde” is set for the str property.

<property name="str" value="abcde" />
java.lang.String[]

When configuring a value in java.lang.String [] type, configure the value in value attribute using the comma (,) delimiter. Values separated by commas will be one element of the array.

In this example, “a, b, c, d, e” is set for the array property. The delimiter, cannot be set as an element.

<property name="array" value="a, b, c, d, e" />
java.lang.Integer(int)

When configuring a value in java.lang.Integer type and int type, describe the value to be configured in the value attribute. The value that can be configured is the value that can be converted by Integer#valueOf.

In this example, “12345” is configured to the num property of Integer (int) type.

<property name="num" value="12345" />
java.lang.Integer[](int[])
Similar to java.lang.String [] type, configure the value in the value attribute using the comma (,) delimiter. The value that can be configured in each element is the value that can be converted by Integer#valueOf.
java.lang.Long(long)
Similar to java.lang.Integer(int), describe the value to be configured in value attribute. The value that can be configured is the value that can be converted by Long#valueOf.
java.lang.Boolean(boolean)

When configuring a value in java.lang.Boolean type, describe the value to be configured with literal in the value attribute. The value that can be configured is the value that can be converted by Boolean#valueOf.

In this example, “true” is configured to the bool property of Boolean(boolean) type.

<property name="bool" value="true" />

7.2.3.5. Use list or map as the configuration value

By configuring the component using list element and map element, setter can be injected for the property receiving list or map.

Configuration of the list using the list element

List element can be set to a string or any Java Beans object.

In this example, list of strings with [string1, string2, string3] is configured for the stringList property of SampleBean.

<component class="sample.SampleBean">
  <property name="stringList">
    <list>
      <value>string1</value>
      <value>string2</value>
      <value>string3</value>
    </list>
  </property>
</component>

An any name can be configured for the list element and the name can be referenced in the property element. The configuration of this example is the same as the above example.

<list name="strList">
  <value>string1</value>
  <value>string2</value>
  <value>string3</value>
</list>

<component class="sample.ListSample">
  <!-- Configure a List named strList -->
  <property name="stringList" ref="strList" />
</component>

In this example, list of Java Beans objects with SampleHandler1, SampleHandler2 and SampleHandler3 is configured for the handlers property.

The name can be referenced by using the component-ref element, which is also shown in the following example.

<component name="sampleHandler3" class="sample.SampleHandler3" />

<component class="sample.ListSample">
  <property name="handlers">
    <list>
      <component class="sample.SampleHandler1" />
      <component class="sample.SampleHandler2" />
      <component-ref name="sampleHandler3" />
    </list>
  </property>
</component>
Map configuration using the map element

In this example, map with “{key1=1, key2=2, key3=3}” in the entry is configured for the map property.

<property name="map">
  <map>
    <entry key="key1" value="1" />
    <entry key="key2" value="2" />
    <entry key="key3" value="3" />
  </map>
</property>

An any name can be configured for the map and the name can be referenced in the property element. The configuration of this example is the same as the above example.

  <map name="map">
    <entry key="key1" value="1" />
    <entry key="key2" value="2" />
    <entry key="key3" value="3" />
  </map>

<component class="sample.ListSample">
  <!-- Configure a Map named map -->
<property name="map" ref="map">
</component>

An any Bean can be configured as Map value by using value-component element.

<property name="settings">
  <map>
    <entry key="sample1">
      <value-component class="sample.SampleBean1" />
    </entry>
    <entry key="sample2">
      <value-component class="sample.SampleBean2" />
    </entry>
  </map>
</property>

Important

When multiple name attributes of map or list are defined, the one defined first is valid. Note that this is a different behavior from bean overwrite.

To change the map or list information for each environment, change the file to be read for each environment.

7.2.3.6. Inject components automatically

Provide a function to automatically inject a component even if the property tag definition of the component is omitted. Automatic injection type can be specified for this function by using autowireType attribute of the component element.

Important

The following are the problems when using the automatic injection function, explicitly specifying None in autowireType attribute is recommended.

  • The state of the final generated object cannot be read from the component configuration file (xml).
  • If the property definition of an optional item is omitted, an object that is not expected may be automatically injected.
  • When automatic injection by type is used and object configuration of the same type are increased during derivation development, maintainability is poor as it requires property to be defined.

The types that can be specified for the autowireType attribute are as follows.

ByType
Automatically injects the component if there is only one type of that property in the DI container.Inject components automatically. This type is is used by default.
ByName
A component with the same name as the property name is automatically injected. An error occurs if the property and component type does not match.
None
Automatic injection is not performed.

An example of automatic injection with the default (ByType) configuration is shown below.

Create an injection target class

Create an injection target interface and implementation class. Although the interface is created in this example, it is not required.

public interface SampleComponent {
}

public class BasicSampleComponent implements SampleComponent {
}
Create a class that uses the object to be injected

Create a class that processes using the class created above. This class receives the above class by setter injection.

public class SampleClient {
  private SampleComponent component;

  public void setSampleComponent(SampleComponent component) {
    this.component = component;
  }
}
Define component in component configuration file

In this example, sampleComponent property is not defined in SampleClient, but since there is only one configuration in the class implementing `SampleComponent`, BasicSampleComponent is automatically configured in the sampleComponent property.

<component name="sampleComponent" class="sample.BasicSampleComponent" />

<component name="sampleClient" class="sample.SampleClient" />

The above configuration is the same as when the property is explicitly defined as given below.

<component name="sampleComponent" class="sample.BasicSampleComponent" />

<component name="sampleClient" class="sample.SampleClient">
  <property name="sampleComponent" ref="sampleComponent" />
</component>

7.2.3.7. Split the component configuration file (xml)

The xml file size increases significantly if all the definitions are defined in one component configuration file, which causes the problem of poor maintainability. Therefore, a function to split the xml file into multiple files is provided.

When splitting the xml file, it is better to split the file based on functional units, etc. The split xml file can be read using the import element.

An example is shown below.

In the following example, 3 xml files are loaded.

<import file="library/database.xml" />
<import file="library/validation.xml" />
<import file="handler/multipart.xml" />

7.2.3.8. Set the dependent value

Values (database connection information, directory path, etc.) that differ between the test and production environments can be managed in the environment configuration file.

Describe the environment configuration file in the simple key-value format as given below. For detailed description rules, see Rules for describing environment configuration file.

database.url = jdbc:h2:mem:sample
database.user = sa
database.password = sa

Important

Note that if the key value of the environment configuration value is duplicated, the one defined later will be valid.

An example is shown below.

Environment dependent value
database.url = jdbc:h2:mem:sample
database.user = sa
database.password = sa

7.2.3.9. Reference environment dependent value from the component configuration file

The environment configuration file can be read from the component configuration file (xml) and used as the configuration value of Java Beans object.

When configuring (injection) the environment dependent value for the object managed by the DI container, describe the key value of the environment dependent value in the component configuration file by enclosing with ${ and }.

Note that this notation cannot be used in the configuration files. (other environment dependent values cannot be referenced in the environment configuration file.)

An example is shown below.

Environment configuration file
database.url = jdbc:h2:mem:sample
database.user = sa
database.password = sa
Component configuration file

Config-file element is used to read the environment configuration file. The file can be read by specifying the file name as in this example, or all the files under a specific directory can be read at once.

When the name of the environment configuration file is “database.properties”, “jdbc:h2:mem:sample” is configured in the url of JdbcDataSource.

<!-- Reading the database.properties file -->
<config-file file="database.properties" />

<component class="org.h2.jdbcx.JdbcDataSource">
  <property name="url" value="${database.url}" />
</component>

There are two types of environment configuration files, config file and properties file. The config file is parsed by independent specifications of Nablarch, and the properties file is parsed by java.util.Properties. Since the config file is an independent specification of Nablarch, the properties file is recommended as the environment configuration file.

For specifications of the environment configuration file, refer to Rules for describing environment configuration file.

Important

Throws ConfigurationLoadException if a key with an environment dependent value that is not defined in the configuration file is referenced from the component configuration file.

7.2.3.10. Overwrite environment dependent values using system properties

Environment dependent value can be overwritten with the system property (value that can be acquired by java.lang.System#getProperties()). Since the system property has priority over the value set in the environment configuration file, the configuration value can be easily overwritten with the vm option.

For example, to change the configuration value only for a specific batch application, the system property can be used to overwrite the environment dependent value.

An example is shown below.

Environment configuration file

Message= Message to be overwritten
Overwrite values with system properties

By configuring the system property with the -D option of Java command, the value of the environment configuration file can be overwritten. In this example, the value of message is “message which will be overwritten”.

java -Dmessage= Message which will be overwritten

7.2.3.11. Overwrite environment dependent values using OS environment variables

With the settings described below, you can override environment dependent values with OS environment variables.

How to enable overwriting by OS environment variables

The mechanism for overriding environment dependent values is implemented by a class that implements the Externalsized ExternalizedComponentDefinitionLoader interface.

This implementation class is loaded using java.util.ServiceLoader. If no service provider has been set, SystemPropertyExternalizedLoader is used by default. This class is a class for overwriting by system properties, and the overwriting by system properties described in the previous section is implemented by this class.

To override environment-dependent values with OS environment variables, use the OsEnvironmentVariableExternalizedLoader as an implementation class.

The concrete configuration is as follows:

  1. Create a directory named META-INF/services directly under the classpath
  2. In the directory created above, create a text file named nablarch.core.repository.di.config.externalize.ExternalizedComponentDefinitionLoader
  3. In the file, list the fully qualified name of the implementation class to be used, separated by a new line

For example, to use an OsEnvironmentVariableExternalizedLoader, the content of the nablarch.core.repository.di.config.externalize.ExternalizedComponentDefinitionLoader is described as follows:

nablarch.core.repository.di.config.externalize.OsEnvironmentVariableExternalizedLoader

When you combine multiple implementation classes, you can also enumerate them with a line separator as shown below.

nablarch.core.repository.di.config.externalize.OsEnvironmentVariableExternalizedLoader
nablarch.core.repository.di.config.externalize.SystemPropertyExternalizedLoader

If multiple implementation classes are specified, they are overwritten in order from the top. Therefore, when an environment dependent value with the same name is overwritten by each method, the class described at the bottom is finally adopted. In the case of the above example, a value set in a system property takes precedence over a value set in an OS environment variable.

About the names of OS environment variables

On Linux, you cannot use . or - in the name of the OS environment variable. Therefore, it is not possible to define OS environment variables to override an environment dependent value with a name like example.error-message.

In order to avoid this problem, Nablarch searches for OS environment variables after performing the following transformations to the names of environment dependent values.

  1. Replace . and - with _
  2. Convert alphabet to uppercase

That is, the environment dependent value named example.error-message can be overridden by defining an OS environment variable named EXAMPLE_ERROR_MESSAGE.

On Windows, you can use . and - as OS environment variables, but the above conversion process is performed regardless of the OS at the time of execution. Therefore, the OS environment variable to override example.error-message must be named EXAMPLE_ERROR_MESSAGE on Windows as well.

7.2.3.12. Inject the object created by the factory class

If the class is implemented as Java Beans, a value can be configured using setter injection and an object can be generated. However, there are cases where objects, which are not implemented as Java Beans such as those provided by the vendor or OSS, have to be managed in the system repository.

In this case, these classes can be managed in the system repository by creating a factory class and then creating an object through the factory class.

The procedure is shown below.

Create a factory class

The factory class is created by implementing ComponentFactory.

Implementation examples
public class SampleComponentFactory implements ComponentFactory<SampleComponent> {
  // Configuration value for the generated object
  private String configValue;

  public void setConfigValue(String configValue) {
    this.configValue = configValue;
  }

  public SampleComponent createObject() {
    // Create an object.
    // In this example, an object is created using the value that is injected
    // by the setter into this class.
    return new SampleComponent(configValue);
  }
}
Configure the factory class in the component configuration file

The object created by the factory class is automatically configured by configuring the factory class like a normal component.

<!-- Factory class definition -->
<component name="sampleComponent" class="sample.SampleComponentFactory">
  <property name="configValue" value="Configuration value" />
</component>

<!-- Class that configures the object generated by the factory class -->
<component class="sample.SampleBean">
  <!-- Object generated with factory class is configured in the sampleObject property -->
  <property name="sampleObject" ref="sampleComponent" />
</component>

Important

Nablarch does not support nesting of factory classes. That is, the properties of a factory class can not specify other factory classes.

<component name="sampleComponent" class="sample.SampleComponentFactory">
  <!-- Nesting of the factory class -->
  <property name="property">
    <component class="sample.OtherSampleComponentFactory">
  </property>
</component>

In this case, building objects within one factory class, including objects to be built in nested factory classes, or Create a class such as Creator/Builder/Provider to generate objects to be built in the nested factory class, and Corresponding to the injection as a component.

7.2.3.13. Constructing objects of annotated classes

Adding SystemRepositoryComponent to the class allows it to be managed in DI container without having to write the settings in XML.

Important

This feature is not available on some web application servers that manage the resources under the classpath in their own file system.

For example, Jboss and Wildfly cannot search for classes annotated with the SystemRepositoryComponent annotation because the resources under the classpath are managed by a virtual file system called vfs.

If such a web application server is used, the component definitions should be defined in XML as usual.

7.2.3.13.1. How to use

Create a class to identify the packages to be collected.

The collection of the class to which SystemRepositoryComponent is assigned is handled by a class that implements the ExternalizedComponentDefinitionLoader interface. This class is an abstract class named AnnotationComponentDefinitionLoader and has an abstract method named getBasePackage that returns the base package for the collection target.

Override the above abstract methods so that collection is carried out in accordance with each project’s package name.

public class ExampleComponentDefinitionLoader extends AnnotationComponentDefinitionLoader {
    @Override
    protected String getBasePackage() {
        return "com.example";
    }
}
Set the created class as a service provider.
Create a file named nablarch.core.repository.di.config.externalize.ExternalizedComponentDefinitionLoader as well as How to enable overwriting by OS environment variables to be loaded with java.util.ServiceLoader and write the fully qualified names of the above classes.
Annotate the classes to be managed in the DI container.

By assigning SystemRepositoryComponent to be managed in a DI container.

@SystemRepositoryComponent
public class ExampleAction {

7.2.3.13.2. Using the constructor injection

SystemRepositoryComponent assigned classes are performed with constructor injection if they fulfill the following conditions at the time of construction.

  • Only one constructor is defined
  • Constructor with arguments

If the conditions are fulfilled, it is injected with the following specifications.

  • Arguments assigned with ConfigValue are injected with a set value
  • The argument given by ComponentRef is the component registered in the DI container is injected
  • If none of the above annotations are assigned
    • If there is only one component on the DI container that matches the argument type, automatically inject that component
    • If no or multiple components matching the argument type exist on the DI container, do not inject anything
Injection of configuration values

The value of the annotation value is injected into the constructor by assigning ConfigValue to the constructor argument. The available types of values are the same as Use a string, numeric, or boolean value as the configuration value.

As in case Reference environment dependent value from the component configuration file, key values of environment-dependent values can be described by enclosing them in ${` and }.

@SystemRepositoryComponent
public class ExampleService {

    private final String errorMessageId;

    public ExampleService(@ConfigValue("${example.service.errorMessageId}") String errorMessageId) {
        this.errorMessageId = errorMessageId;
    }
Injecting components

The component with the name of the annotation value is injected by assigning ComponentRef to the constructor argument.

The following example injects a component named lettuceRedisClientProvider.

@SystemRepositoryComponent
public class ExampleService {

  private LettuceRedisClient client;

  public ExampleService(@ComponentRef("lettuceRedisClientProvider") LettuceRedisClient client) {
      this.client = client;
  }

Tip

The constructor injection is handled by class ConstructorInjectionComponentCreator. By overriding newComponentCreator in AnnotationComponentDefinitionLoader, it can be replaced with a ComponentCreator implementation that handles any processing at object construction time in the annotated class.

public class ExampleComponentDefinitionLoader extends AnnotationComponentDefinitionLoader {
  @Override
  protected String getBasePackage() {
      return "com.example";
  }

  @Override
  protected ComponentCreator newComponentCreator() {
    // Change to any ComponentCreator implementation class.
    return new ExampleComponentCreator();
  }
}

7.2.3.13.3. Managing the Action class in a DI container

Annotations can be assigned to the Action class to be managed in a DI container. In the dispatch handlers (Routing Adapter, Request Dispatch Handler, HTTP Request Dispatch Handler) provided by Nablarch, the class to be dispatched is instantiated in the dispatcher. Therefore, when registering an Action class to the DI container, it is necessary to replace DelegateFactory to obtain the dispatch target class from the system repository. The replacement is set up using DispatchHandler#setDelegateFactory as follows:

<component name="packageMapping" class="nablarch.integration.router.RoutesMapping">
  <!-- DelegateFactory to get the dispatch destination from the system repository -->
  <property name="delegateFactory">
      <component class="nablarch.fw.handler.SystemRepositoryDelegateFactory"/>
  </property>
  <!-- Other properties omitted -->
</component>

7.2.3.14. Initialize the object

The following steps are required for the initialization process of the object.

  1. Implement the Initializable interface.
  2. Configure a list targets for initialization in the component configuration file.

The detailed procedure is shown below.

Implement Initializable interface

Initialize with initialize.

public class SampleComponent implements Initializable {
  public void initialize() {
    // Initialize based on the value injected into the property
  }
}
Configure a list targets for initialization in the component configuration file

Configure the object to be initialized to BasicApplicationInitializer.

If information of the initialization order of the object to be initialized is required, configure the object that is to be initialized first to a higher order. For the configuration example given below, initialization is performed in the following order.

  1. sampleObject1
  2. sampleObject3
  3. sampleObject2

Important

Set the component name of BasicApplicationInitializer to initializer.

<!-- Configure the object to be initialized -->
<component name="sampleObject1" class="sample.SampleComponent" />
<component name="sampleObject2" class="sample.SampleComponent2" />
<component name="sampleObject3" class="sample.SampleComponent3" />

<component name="initializer"
    class="nablarch.core.repository.initialization.BasicApplicationInitializer">

  <!-- List the objects to be initialized with the list element in the initializeList property-->
  <property name="initializeList">
    <list>
      <component-ref name="sampleObject1"/>
      <component-ref name="sampleObject3" />
      <component-ref name="sampleObject2" />
    </list>
  </property>

</component>

7.2.3.15. Handling the disposal of objects

In order to perform the disposal process of an object, the following steps are required.

  1. Implement the Disposable interface.
  2. Set the list of discarded targets in the component configuration file.

Detailed steps are shown below.

Implementing the Disposable Interface

Disposal process in dispose.

public class SampleComponent implements Disposable {
  public void dispose() throws Exception{
    // Release of resources, etc., for the disposal process
  }
}
Set the list of discarded targets in the component configuration file

Set the object to be discarded to BasicApplicationDisposer.

If the discard order of the objects to be discarded needs to be considered, set the objects which want to be discarded first to down. In the case of the example configuration below, the disposal process is done in the following order.

  1. sampleObject1
  2. sampleObject2
  3. sampleObject3

Important

The component name of BasicApplicationDisposer must be disposer.

<!-- Setting up the objects to be discarded -->
<component name="sampleObject1" class="sample.SampleComponent1" />
<component name="sampleObject2" class="sample.SampleComponent2" />
<component name="sampleObject3" class="sample.SampleComponent3" />

<component name="disposer"
    class="nablarch.core.repository.disposal.BasicApplicationDisposer">

  <!-- Enumerate the objects to be discarded by the list element in the disposableList property -->
  <property name="disposableList">
    <list>
      <component-ref name="sampleObject3" />
      <component-ref name="sampleObject2" />
      <component-ref name="sampleObject1" />
    </list>
  </property>

</component>

The BasicApplicationDisposer has a method, addDisposable, to which any Disposable can be added after the component is created.

The Disposable added in this addDisposable is expected to be added in the order in which its instance was created.
In that case, the discard process should be done in the opposite order of instance creation (e.g., JDBC Connection, Statement, and ResultSet).

For this reason, BasicApplicationDisposer calls the disposal process in the opposite order to that set in the disposableList.

Set the Closeable object in the list of objects to be discarded

If a component implements java.io.Closeable, it can easily be set up in the list of discarded items by using DisposableAdaptor

<!-- Components that implement java.io.Closeable -->
<component name="closeableComponent" class="sample.CloseableComponent" />

<component name="disposer"
    class="nablarch.core.repository.disposal.BasicApplicationDisposer">

  <property name="disposableList">
    <list>
      <component class="nablarch.core.repository.disposal.DisposableAdaptor">
        <!-- Set the target property of DisposableAdaptor to a component that implements Closeable -->
        <property name="target" ref="closeableComponent" />
      </component>
    </list>
  </property>

</component>

7.2.3.16. Configure the DI container information to the system repository

By loading the information of the DI container into the system repository, the objects in the DI container from all points in the application can be accessed.

An example of loading and configuring the component configuration file in the system repository is shown below.

In this example, information of the DI container constructed based on web-boot.xml is configured in the system repository.

XmlComponentDefinitionLoader loader
    = new XmlComponentDefinitionLoader("web-boot.xml");
SystemRepository.load(new DiContainer(loader));

Important

The process of registering DI container information in the system repository is implemented by the following classes provided by Nablarch. Therefore, there is basically no individual implementation.

  • Implementation class of ServletContextListener
  • Launch class of independent application

7.2.3.17. Get object from system repository

To acquire the object from the system repository, use the class SystemRepository.

The DI container information must be configured in the system repository in advance. For details, see Configure the DI container information to the system repository.

Object can be acquired by specifying the value of name attribute configured in the component element (including list and map elements) as shown below.

Component definition
<component name="sampleComponent" class="sample.SampleComponent" />

<component name="component" class="sample.Component" >
  <property name="component2">
    <component name="component2" class="sample.Component2" />
  </property>
</component>
Acquisition example
// Get using SystemRepository#get.
SampleComponent sample = SystemRepository.get("sampleComponent");

// Obtain nested component by concatenating the parent name and its own name with ".".
Component2 component2 = SystemRepository.get("component.component2");

7.2.4. Rules for describing environment configuration file

There are two types of environment configuration files, config file and properties file. The description rules of each environment configuration file are explained.

Specifications of properties file
Analyzed based on the Java Properties specifications.
config file specifications

The specifications of the config file are described below.

Description format of setting value

The configuration value is described by separating the key and value with =.

key1=value1
key2=value2
Comment description

Only line comments using # is supported. If # is present in a line, the rest of the line is considered as a comment.

# This is a comment
key = value   # This is a comment
Description of configuration values that spans multiple lines

By using \ at the end of the line, the configuration value can be described over multiple lines.

In the case of the example below, the combinations of configuration values are as follows.

  • key -> value
  • key2 -> value,value2
  • key3 -> abcdefg
key = value
key2 = value,\
value2
key3 = abcd\    # Comments can be defined here
efg
Reserved word escape

\ is used to escape when handling the following reserved words as general characters.

  • #
  • =
  • \

In the case of the example below, the combinations of configuration values are as follows.

  • key -> a=a
  • key2 -> #This is not a comment
  • key3 -> a\b
key = a\=a
key2 = \# This is not a comment
key3 = a\\b

Tip

Only the value of half-width space is not supported in the config file, but it can be handled by configuring the numeric reference character in the properties file.

key = \u0020

Tip

Empty value is not supported in the config file, but it is treated as an empty character in the properties file.
Therefore, care must be taken when referring to environment-dependent values from the component configuration file because the behavior is different.

In the case of the following component definition, describe the behavior of the config and properties files.

  • If the configuration file is a config file, config.value does not exist and an exception will be thrown.(※)
  • If the configuration file is a properties file, the property of the component is set to an empty string.
<property name="property" value="${config.value}" />
# empty value
config.value=

※Prior to Nablarch5u18, if the configuration value does not exist, no exception will be thrown, and a WARNING level log will be output and property will be set to the string “${config.value}”.