Not sure if this question was previously asked, but I cannot seem to find the answer.
Where does one store the properties when loaded in a webapp.
I have a web application which has settings to allow system administrators via a user interface to change settings in the app.
For example the app allows only selected user groups to be able to go a certain page.
I wanted to allow system admins to go in and set the user groups that can go to the above mentioned page, and then change it at a later date.
The issue I'm facing is that once loaded the properties file, where do I store the data rather than continuously loading the properties file each time a user goes into the page.
I'm probably not getting the full concept of how properties are used so any guidance would be greatly appreciated.
Just make sure I can read the user groups in, can change the user groups without reloading the class/app and allow it to be thread safe and quick without two different threads having two different properties because we are using a load balanced environment. With a content share which is where the properties files are stored and accessed (not having any issues with this so not looking for help with where to store the properties file).
Any help greatly appreciated.
EDIT 1
The application runs on a clustered environment which means that other application servers could potentially have different values due to multiple ServletContexts.
Register ServletContextListener to load Init parameters and properties at server start-up.
Load properties at single time and make it visible to other classes statically or you can store it in application context as well to access it from anywhere such as JSP and Servlet.
Note: Make the properties file location configurable in web.xml rather than hard-coding it in Java class. You can retrieve the properties file location as system environment variable as well.
Sample code:
public class AppServletContextListener implements ServletContextListener {
private static Properties properties;
#Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
String cfgfile = servletContextEvent.getServletContext().getInitParameter("config_file");
properties.load(new FileInputStream(cfgfile));
// store it in application scope as well
servletContextEvent.getServletContext().setAttribute("prop",properties);
}
public static Properties getProperties(){
return properties;
}
}
web.xml:
<listener>
<listener-class>com.x.y.z.AppServletContextListener</listener-class>
</listener>
<context-param>
<param-name>config_file</param-name>
<param-value>config_file_location</param-value>
</context-param>
Please have a look at my another post that is asked in the same context:
Retrieve Init parameters outside servlet
EDIT
If you are changing the properties at run-time then don't use Servlet context according to the ServletContext javadoc:
In the case of a web application marked "distributed" in its deployment descriptor, there will be one context instance for each virtual machine. In this situation, the context cannot be used as a location to share global information (because the information won't be truly global). Use an external resource like a database instead.
The Servlet specification also states in "SRV.4.4.1 Context Attributes in a Distributed Container":
Context attributes are local to the JVM in which they were created. This prevents ServletContext attributes from being a shared memory store in a distributed container. When information needs to be shared between servlets running in a distributed environment, the information should be placed into a session (See Chapter SRV.7, “Sessions”), stored in a database, or set in an Enterprise JavaBeansTM component.
In that case you can try with some third party cache that works in distributed environment as well as mentioned below:
EHCache
Infinispan
OR store all the properties in the database.
The Servlet container offers the concept of Contexts. I find it helpful to consider a Context as a useful box for storing things in, and operates like a Map.
There are a number of different Contexts available to a Java Webapp, and they differ in scope (that is, how long the data held in the context lasts for, and where it can be accessed from). There is the Page Context, the Session Context and the Servlet Context
The Page Context has the narrowest scope, and is only lasts as long as a single page takes to process.
The Session Context has a greater scope, and lasts as long as single user session, i.e. multiple requests from a browser. It is useful if your webapp requires authentication - information about the authenticated user will be stored in the Session Context.
The Servlet Context is effectively global and is always available to the whole application. This is where I would recommend storing configuration properties which effect the functioning of the application.
In a Servlet, you may access the Servlet Context like this:
ServletContext context = request.getSession().getServletContext();
You can store something in the context like this:
context.setAttribute("key", object);
Where key is a String - the name of the attribute.
You may retrieve it again like this:
object = context.getAttribute("key");
Which returns an Object. You may cast it to whatever type it really is. If you want to, you can store a Properties object in it:
Properties props = //... get the properties from file
context.setAttribute("props", props);
And then retrieve them:
Properties props = (Properties) context.getAttribute("props");
Or you can store the individual properties as separate attributes in the context.
All contexts are accessed the same way.
You could go with the classic singleton pattern, where you have a single ApplicationProperties class which holds globally valid values for your application backed by a property file so no part of your application has to care about how to store the properties. Pseudo code:
public class ApplicationProperties {
private static final String PATH = "app.properties";
private static final ApplicationProperties INSTANCE = new ApplicationProperties();
private String userGroup;
private ApplicationProperties() {
// Load properties from PATH and populate fields.
this.userGroup = ...
}
public static ApplicationProperties getInstance() {
return INSTANCE;
}
public String getUserGroup() {
return this.userGroup;
}
public String setUserGroup(String userGroup) {
// Save to property file to persist.
this.userGroup = userGroup;
}
}
You just have to synchronize access to the fields that no two threads overwrite properties and create race conditions.
Related
I am absolutely confused with application context in spring. If i use spring (simple spring) create a beans.xml and then invoke Application context from (for example) main() method.
ApplicationContext context = new FileSystemXmlApplicationContext
("C:/Users/ZARA/workspace/HelloSpring/src/Beans.xml");
all works well. But I don't understand if i move file on directory above or in another directory(for example) it will be ok?
in spring-mvc there is context for each DispatcherServlet which i create and where i specify some beans, there is common context for all servlets, how to specify this? in web.xml?
in general, please explain me this moment (I read spring in action, i undesrstand almost all, but these tricky moment isn't shown there.
From FileSystemXmlApplicationContext java doc:
Standalone XML application context, taking the context definition files from the file system or from URLs, interpreting plain paths as relative file system locations (e.g. "mydir/myfile.txt"). Useful for test harnesses as well as for standalone environments.
The key words here are context definition files, so you can pass paths to as many xml-files, as you want. Besides that, you can create an application context and pass it to the new one as a parent:
FileSystemXmlApplicationContext(String[] configLocations, ApplicationContext parent)
Thus you can easily create the needed hierarchy of contexts.
ApplicationContext parentContext = new FileSystemXmlApplicationContext
("C:/some/path/ParentBeans.xml");
ApplicationContext childContext = new FileSystemXmlApplicationContext
(new String[]{"C:/some/path/ChildBeans1.xml", "C:/some/path/ChildBeans2.xml"}, parentContext);
if i move file on directory above all in another directory(for example) it will be ok?
As long as your path to file is correct and reachable - it's Ok.
I've got a couple of class instance variables: userSF which is a SessionFactory and userCfg which is a Configuration set up to use user.cfg.xml. When I have someone log into my application their database login information is grabbed from a file the following happens:
userCfg.setProperty("hibernate.connection.username", username);
userCfg.setProperty("hibernate.connection.password", password);
userSF = userCfg.buildSessionFactory();
When the user logs out of the application it closes the session factory. then when another user tries to log in that same code is called with username and password changing based on who is logging in. The problem is that the second time it's called I'm getting an exception:
org.hibernate.HibernateException: HHH000469: The ClassLoaderService can not be reused. This instance was stopped already.
Is there a way to reuse the SessionFactory variable for multiple logins?
You should not create a SessionFactory for each logged in user / configuration. It is a very costly component and should target the Singleton pattern. Use a single instance per context within your application.
If you have multiple configurations you can cache multiple instances in application scope. Depending on your application this maybe a static Singleton class using a Map<MyUserPasswordKey, SessionFactory> or in an JAVA-EE container a #ApplicationScoped EJB or JSF-Bean.
In case a context based on a user-password key will lead to too many SessionFactory instances you maybe should change your application to either manage them in permission groups or restrict your functionality within your data access or service layer.
What is the best approach for creating services that load a property set at runtime (bean is passed "xyz" and loads xyz.properties)? These properties files need to be able to be dropped into a folder outside the classpath before a command is entered to start the service (edit: this could happen at any time while the program is running).
I already have a system to do this that we've been using for over a year, but I'm migrating to spring to make the code more modular (customize services more easily through DI) and easier to maintain. My current method of creating an environment and then passing it with "this" to the dependencies just seems upside down from an IoC standpoint.
Is there a way to use a PropertyPlaceholderConfigurer without hardcoding the name of the property file? Maybe just a reference to a variable I pass into the constructor of the service that its dependencies can load? So far, it looks like I will have to create a service and inject its dependencies without any config and then call a separate load method for each to pass in the properties, but that just seems like I'm not really using spring.
USE CASE: The app will pool client connections to various servers and will forward requests from other applications to these servers. New profiles must be able to be added by non-programmers without taking down or restarting the app. The profiles will include basic things like host, port, and login info, but also more complex things like whether to use tcp/http, ssl/https (which will determine which client type to use), and timeouts and pool min/max/etc (which will need default values).
I tried with PropertyPlaceholderConfigurer and frankly, I couldn't wrap my head around it, somehow. It's easy enough to use when you use the existing options but I couldn't extend the framework.
So my approach was much more simple:
Create an annotation #InjectConfig which takes a config key as parameter.
In your beans/services, annotate fields or public setters with this annotation.
Write a BeanPostProcessor which takes options from a "config provider" and injects them into the fields / setters.
Now all you need is a config provider. Inject that into the post processor and you're done.
Note: I prefer annotating setters because that means you can easily configure your services from tests (just call the setters) without having to come up with smart names for 238576 config files.
EDIT If you have many configs, then a config factory might be a better choice:
Create a key to describe a config bundle (I usually use an enum or a new type here to prevent typos)
Put this key into the service when you create it (manually or via Spring)
Write a config factory that can return Properties or a Map for a config key.
Inject this factory into your service
In the init code of your service, use the key to lookup your config via the factory.
Using this approach, you can have a dummy factory that always returns the same thing in tests and a more complex factory for production.
The real factory can then be configured via spring so it knows where to look for configuration files. One approach is to register a java.io.File per config key. Now your concerns (configuring a service and loading configs) are completely separated.
PropertyPlaceholderConfigurer reads and initialize files on application context initialization and only once. So most probably you cannot configure it at runtime.
But you can have variables. For example, for my case I have default properties and user specific properties. So PropertyPlaceholderConfigurer loads properties from classpath first and after that is trying to find additional properties at defined location (user home folder). I user's property file exists so configurer loads it and override properties.
Here is my example:
<bean id="config" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true"/> <!-- do not throw exception if file not found -->
<property name="locations">
<list>
<value>classpath:server.properties</value>
<value>file:${user.home}/user.properties</value>
</list>
</property>
</bean>
I'm not sure that this answer is what you're exactly need. But I'm trying to guess what is your actual task. So if you need re-read properties runtime each time you access them you have to do it manually like you did before because spring application context helps you configure your application initial configuration.
It seems like the best approach may be to use a ServiceManager that contains the main ApplicationContext and then have each Service initialize its own FileSystemXmlApplicationContext with the main context as the parent like so:
public class ServiceManager {
ApplicationContext appContext;
String APP_HOME = System.getProperty("user.home") + File.separator;
public void init() {
//set main spring context
appContext = new AnnotationConfigApplicationContext(AppConfig.class);
}
public void start(String serviceName) throws Exception {
ApplicationContext serviceContext = new FileSystemXmlApplicationContext(
new String[]{APP_HOME + serviceName + ".xml"}, //path to child ctx
appContext); //reference to parent ctx to build hierarchy
Service service = (Service) serviceContext.getBean("service");
service.start();
}
}
The ApplicationContext is a bit heavy to be duplicating, but memory is pretty cheap these days and this provides total separation of concerns. I still have shared logging and an event system managed by the parent context, and each service is now simplified in its own config. I built a proof of concept using two services, and it seems to work fine so far. I'll add another comment once I finish the other services and finish testing.
reference:
http://techo-ecco.com/blog/spring-application-context-hierarchy-and-contextsingletonbeanfactorylocator/
I'm developping a program that uses JPA, and I deployed it in a single jar. I wish to modify (or ask to the user, in an menú item) the configuration of the connection data with the database server.
It's possible to modify the data (user, password, ip) of the server "on the fly"?
(I apologize my bad english)
how about making a HashMap<String,Object> containing your options and passing it to Persistence.createEntityManagerFactory("unitName",map)?
From the Docs:
public static EntityManagerFactory createEntityManagerFactory(String persistenceUnitName, Map properties)
Create and return an EntityManagerFactory for the named persistence unit using the given properties.
Source
The JPA spec doesn't allow for dynamic modification of persistence-units. Some implementations may provide an implementation-specific way of defining a persistence-unit dynamically. With DataNucleus JPA we do it as per the foot of this page
I'm writing a Java servlet that needs to read some site-specific
configuration data; I would like it to be easily accessible/modifiable
by the sysadmins at deployment time. There is no sensible default,
so the data has to be provided by the site admin.
It consists of a few string key/value pairs (think Properties).
It would only be read once (at initialization time).
I'm aware of this SO question
and the ServletContext.getInitParameter() mechanism, but as far as
my understanding goes, they require the data to be bundled in the
servlet package (either as a properties file, or specified in the
web.xml), which makes it inconvenient to upgrade the servlet code.
Is there any "standard" interface for a servlet to get this kind of
key/value configuration data? It would be ok if the programming
interface is the same everywhere, but the actual way of setting the
configuration data depends on the actual servlet container being used.
I'm looking preferably at portable solutions, but I'd be content with
something that only works in Tomcat and Jetty.
The recommended way to configure an application server for a web application is per JNDI.
Every application server (including Jetty and Tomcat) allows you to configure JNDI parameters.
For Jetty you can add the following to your jetty.xml to add the JNDI parameter param.file:
<!-- JNDI java:comp/env -->
<New id="param.file" class="org.mortbay.jetty.plus.naming.EnvEntry">
<Arg>param.file</Arg>
<Arg type="java.lang.String"><SystemProperty name="jetty.home" default="."/>etc/config.properties</Arg>
<Arg type="boolean">true</Arg>
</New>
Then in your servlet you can read the JNDI parameter:
import javax.naming.InitialContext;
import javax.naming.NamingException;
...
public Object readJndi(String paramName) {
Object jndiValue = null;
try {
final InitialContext ic = new InitialContext();
jndiValue = ic.lookup("java:comp/env/" + paramName);
} catch (NamingException e) {
// handle exception
}
return jndiValue;
}
public String getConfigPath() {
return (String) readJndi("param.file");
}
The way to set JNDI values differs for other application servers but the code to read the configuration is always the same.
The Servlet init parameters are the right (and standardized) way of defining properties which can be configured by the administrator. Many of the application servers provide a GUI backend where the parameters can be configured.
For an example for Tomcat, see Defining Tomcat servlet context parameters
Configure the external location of the properties - either via a jvm argument (when starting the servlet container), or in the web.xml
in the external location use config.properties and read it with java.util.Properties
You may take Preferences or hack with user.home, user.dir, etc. But for a few key/value keep things simple.
Write a small Singleton to wrap around Properties and load them from a fix & absolute location
public class LocalConfig extends Properties {
public static LocalConfig $ = new LocalConfig();
private LocalConfig() throws IOException {
load(new File("/etc/myconfig.properties"));
}
}