Nowadays, there are many options for building Java applications and their configuration tasks are solved in different ways.
The article will discuss the techniques and features of configuring J2SE and J2EE applications using standard JDK tools, as well as alternatives to these methods.
J2se
java.util.Properties
The classic way to configure applications is to use the
java.util.Properties class. Inside, everything is very simple - in fact, this is an extension of the
java.util.Map interface with the ability to save and initialize default values.
There are several minuses:
- No typing - getProperty returns only String;
- Tracking changes in the configuration file is not supported (i.e., if a change occurs, no events will be generated and the application will not know anything about them);
- Work with only one file. N files = N instances of Properties.
')
This list of minuses suggests that the use of pure Properties in real production is inefficient. The first thing that can be encountered with this approach is complaints from the maintenance service that making any changes to the configuration requires restarting the application.
The most likely, in my opinion, negative consequences of this:
- Operation will minimize all changes, even at the price of having a certain percentage of errors in the processing of requests. It all depends on the intensity of calls to the application, the profitability of each of them and the ratio of these numbers with the percentage of erroneous calls;
- The blame for the first item will be on the developer - and correctly, because applications should be made so that they can be used effectively;
- Losses from downtime will also be attributed to the curvature of the software, and therefore apply to the developer of this software;
- The growth of guilt and the gradual loss of credibility with the service of exploitation;
- Growth of negative emotional background
All this can be objected: "You can just add rereading the settings file and the event generation subsystem" - yes, this is so, only all this has already been done and done to the smallest detail, which seem unclear now, but always appear.
In the next article I will talk about the Commons Configuration and how the problems identified above are solved there.
For now, let's consider typical configuration options for J2EE applications.
J2EE-EJB3
Resource injection
One of the easiest ways to configure EJB applications is to use a deployment descriptor (ejb-jar.xml):
< enterprise-beans >
< session >
< ejb-name > MyService </ ejb-name >
< env-entry >
< env-entry-name > myProperty </ env-entry-name >
< env-entry-type > java.lang.String </ env-entry-type >
< env-entry-value > 100500 </ env-entry-value >
</ env-entry >
</ session >
</ enterprise-beans >
In the descriptor we specify the name (env-entry-name), the type (env-entry-type) and the parameter value (env-entry-value), then we inject it using the
Resource annotation:
@Resource(name = "myProperty" )
private String myProperty;
@PostConstruct
public void postConstruct() {
System. out .println( "myProperty = " + myProperty);
}
This approach allows you to work with the parameters of the following types:
- java.lang.String
- java.lang.Boolean
- java.lang.Byte
- java.lang.Character
- java.lang.Double
- java.lang.Float
- java.lang.Integer
- java.lang.Long
- java.lang.Short
The disadvantages include the fact that a change in the parameters leads to a recurring application, which, in turn, leads to a certain period of inoperability.
Redeploy policy depends on the settings of the scanner of application descriptor changes on the application server.
So, for example, in the case of the JBoss 5.x-6.x application server, changing ejb-jar.xml is guaranteed to result in redeployment (unless, of course, the scanner is turned off and rehearse manually, via the JMX console).
Using external settings file
There is a very useful document -
limitations of the EJB technology . This document has clear indications of not using file resources. The indications are as follows:
- File resource is not transactional;
- Files are not a suitable place to store business data because they are beyond the scope of the application server and do not provide the proper level of support for locking mechanisms.
However, using files as read-only data sources is permissible when they are inside EE applications. The fact is that in the case of cluster deployment, the EE application will be the same on all nodes.
Thus, we come to the classic use of java.util.Properties inside EE applications:
@Stateless(mappedName = "BackendService" )
public class BackendServiceBean implements BackendServiceLocal {
private static final String P_PROPERTIES = "myProperties.properties" ;
private static final Logger logger = LoggerFactory.getLogger(BackendServiceBean. class );
@EJB
private DataRepositoryLocal dataRepository;
@Resource(name = "remoteURL" )
private String remoteURL;
private Properties properties;
@PostConstruct
private void init(){
InputStream propertiesResource = null ;
try {
propertiesResource = getClass().getResourceAsStream(P_PROPERTIES);
properties = new Properties();
properties.load(propertiesResource);
} catch (Exception e) {
logger.error( "Error" , e);
} finally {
if (propertiesResource != null ){
try {
propertiesResource.close();
} catch (Exception e) {
logger.error( "Error" , e);
}
}
}
}
public Properties getProperties() {
return properties;
}
The downsides are the same as those previously mentioned for J2SE and java.util.Properties. Plus, we are in the context of J2EE and cannot simply add a stream that tracks file changes and generates events (since you cannot create threads in a J2EE application).
Someone might say: "We need to re-read the .properties file every time the application calls getProperty on our properties-proxy-object." Yes, this can be done, but in this case you should forget about the high performance of the application - opening the file for reading, loading it into the buffer, parsing and creating an instance of Properties will introduce a noticeable delay in processing.
The correct application of this solution is to store only static read-only settings.
Other options
The options described earlier have a disadvantage - correct work is provided only with static parameters. If it is necessary to obtain always the actual value of any parameter, then you need to look for other options.
For J2EE applications, these options can be:
- Getting settings from the database (and another application is entering the database - for example, admin-configurator);
- Request settings from a remote supplier component (for example, with the name ConfigurationProvider).
For both J2EE and J2SE applications, you can use different frameworks / create your own, customized for solving configuration tasks.
J2EE-servlets
When configuring servlets, we are dealing with a web.xml descriptor that sets the parameters we need:
< web-app version ="2.5" xmlns ="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation ="http://java.sun.com/xml/ns/j2ee java.sun.com/xml/ns/j2ee/web-app_2_5.xsd" >
<!-- -->
< context-param >
< param-name > myContextParam1 </ param-name >
< param-value > value1 </ param-value >
</ context-param >
< context-param >
< param-name > myContextParam2 </ param-name >
< param-value > value2 </ param-value >
</ context-param >
<!-- -->
< filter >
< filter-name > myFilter </ filter-name >
< filter-class > net.universe.filter.EntityAccessFilter </ filter-class >
< init-param >
< param-name > checkType </ param-name >
< param-value > ldap </ param-value >
</ init-param >
< init-param >
< param-name > myParam </ param-name >
< param-value > true </ param-value >
</ init-param >
</ filter >
<!-- -->
< servlet >
< servlet-name > MyServlet </ servlet-name >
< servlet-class > net.universe.servlet.MyServlet </ servlet-class >
< init-param >
< param-name > servletParam1 </ param-name >
< param-value > paramValue1 </ param-value >
</ init-param >
< load-on-startup > 1 </ load-on-startup >
</ servlet >
</ web-app >
The setting is to change the param-value configuration items. After changes and saving the file, we also have a redeploy application with a subsequent period of inoperability. This configuration method, as well as the variant with ejb-jar.xml, is most suitable for parameters that are not intended to change in the course of the application. Techniques for working with runtime settings here are similar to those used in the case of EJB - database, JNDI, etc ...
Common to all
System.getProperty
Common to all the listed configuration methods is the use of system parameters passed in the launch string of a Java application:
java -DmyProperty1=myPropertyValue1 -DmyProperty2=myPropertyValue2 -jar myapp.jar
After this, the configuration parameters can be taken using the java.lang.System class:
String string = System.getProperty("myProperty1");
This method is limited in the context of working with J2EE - when operating in cluster mode, the system variable may not arrive at all nodes. Why “can” - because, for example, the JBoss application server has the SystemPropertiesService service and fragments can be included in our EE application by configuring it (i.e. the system variable will end up on all nodes, because in the configuration files in the application).
Quite often, this configuration method is used to quickly add some new check conditions to the code if necessary.
For example, it is so fast that there is only time for adding conditions, compiling a class and replacing it in an already deployed EE application / library with subsequent redeploying / restarting.
Of course, this option can not be called good practice, but nevertheless this happens in real life.
You can also note an alternative to this approach - the use of aspect-oriented programming / injection in byte-code. These techniques allow the original application to remain unchanged, but require a higher level of development skills, especially when it comes to the dynamic implementation of AOP interceptors on a production system that is running.
Jmx
JMX is a convenient tool for a lot of things, including for configuring applications. You can combine the application of java.util.Properties/Commons Configuration and the exposed MBean with methods for setting / getting the values of our parameters (if set, then delegating to properties.setProperty).
Such a solution can be successfully applied where there is no access to the configuration files, but there is access to the MBeanServer via the network.
The disadvantages of this approach are as follows:
- JMX subsystem is disabled by default in J2SE applications;
- It is acceptable to use only simple types of parameters (complex ones can also be, but then it will not be possible to manage via jconsole);
- In the context of J2EE, working with JMX can take on very sophisticated forms. So, for example, the JBoss 4.x-6.x microkernel uses JMX at its core and an attempt to get an MBean tree in the jconsole utility will, with a high degree of probability, cause it to hang / very slow.
That's all for now.
An article about the Commons Configuration library will be completed soon, which greatly expands the possibilities for working with configuration files in J2SE and J2EE applications.
Thanks for attention!