I was previously hard coding a path to load a json file like so:
JsonParser parser = jf.createJsonParser(new File("/some/path/here/myapp/WEB-INF/settings.json"));
So I tried to extract it out to a properties file like so, files.properties:
path.to.json=/some/path/here/myapp/WEB-INF/settings.json
Now how can I modify my call to jf.createJsonParser to load the file using the path from the files.properties path.to/json key?
I'm a little confused. Your title indicates that you are looking for a Spring solution, but you are not using IOC here, from what I can tell. Otherwise, you would be injecting the JsonParser bean and not instantiating it within your code.
Without seeing the global structure of what you are doing, I would do the following (assuming that you have Spring properly configured).
applicationContext.xml:
<bean id="jsonParser" factory-bean="<jf bean definition>" factory-method="createJsonParer">
<constructor-arg>classpath:/settings.json</constructor-arg>
</bean>
Then within your actual class that uses the JsonParser, have it autowired for simplicity sake:
class myClass{
#Autowired
JsonParser jsonParser;
public void myMethod(){
String data = jsonParser.doSomthingNeatHere()....
}
}
I've assumed that JsonParser is of scope prototype, but you can change it to singleton if that matches your needs more appropriately. As well, I've only given you a little pseudo-code to give you the idea of how to allow Spring to manage the life-cycle of the bean as opposed to you instantiating it yourself.
You can read more about Spring 3 and IOC here if you need additional reference.
If you can put your file into a source folder, do it and try "classpath:/"
Example:
new File( "classpath:/context/settings.json" );
I don't know whether your looking for a Spring specific solution, but I see nothing wrong with using the JCL.
Properties props = new Properties();
props.load(new FileReader(propsFile));
String jsonFile = props.getProperty("path.to.json");
JsonParser parser = jf.createJsonParser(new File(jsonFile));
Related
i've been working with Spring for some time and have a question regarding the very common configuration properties files (like the common application.properties that comes with every spring boot app you initialize). Recently, i also found that configurations like this can be done in yaml files. I have two questions:
When in a application.properties file, we write something like:
# application.properties
spring.debug = true
some-random-value = 15
does this mean that these values will be injected in the application context?
When we write something like:
# application.properties
spring.debug = true
does this mean, that somewhere, there is some class, that has an attribute that looks something like this? -->
#Component
class SomeClass{
#Value("spring.debug")
boolean shouldIRunInDebugMode;
...
}
2.a. If the answer to question 2 is yes, then how can I, looking at something like this:
# application.properties
spring.debug = true
find that class that is expecting that value. The same would apply to if i was looking at something like:
# application.yaml
someThidPartyLibraryName:
shouldWeLog: true
If i see a yaml configuration file, just looking at all the configuration there usually is not enough for me to know what is happening. How can i find the class that is being affected by this configuration, so that i can better understand what this config is doing?
Thank you!
The response is generally yes. If you declare a property in the application.properties or application.yaml is mainly, because you would use it later in the code, for example injecting in some bean with the support of #Value annotation. However, there are also many built-in properties (let's say for example server.port), which you usually don't have to declare and therefore you won't see explicitly in the code. Use an IDE to search the configuration properties and the manual to check the preconfigured ones in case of need.
Your understanding regarding spring value injections from application.properties is correct. #2 - is Yes. Any property from application.properties can be injected to any java class as #Value.
Regarding #2.a - Yaml is just another format on how you organize your variable hierarchy by indentations. That's a superset to the JSON structure.
For example,
in application.properties file you can add something like this
myapp.db.url=<dburl>
myapp.db.username=<dbuser>
myapp.db.password=<dbpassword>
the same can be represented in Yaml in a much efficient manner as below
myapp:
db:
url:<dburl>
username:<dbuser>
password:<dbpassword>
And in either case, for your Jave file you can inject as
#Value("myapp.db.url"
private String dbUrl;
Properties defined in yaml or a properties file may be accessed using the #Value annotation to inject, or using a #ConfigurationProperties class - see https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-typesafe-configuration-properties for complete details.
Finding the property usage is supported by some IDEs - IntelliJ allows you to click through. Otherwise it's a search through the source. For #ConfigurationProperties, once you find the class then just look for code that calls its accessor methods.
Properties files and yaml files are used in Spring Boot for configurations. The main difference between the two is yaml provides structuring/grouping of configurations where as Properties are usually flat and may be repeating the same information:
For example;
Properties file
server.port = 8080
server.host = localhost
yaml file
server:
port: 8080
host: localhost
But in a Spring Boot AutoConfiguration class regardless of yaml or Properties used, a following looking ConfigurationProperties class will be used which will map server.port and server.host
#ConfigurationProperties(prefix = "server")
public class ServerConfiguration {
private int port;
private String host;
}
#Configuration
#EnableConfigurationProperties(ServerConfiguration.class)
public class ServerAutoConfiguration {
}
Hope this answers your questions.
Is there a way to create an ApplicationContext (or whatever else in Spring you can use to do getBean("beanName") ) by passing in an XML file that is in memory? The only methods I've been able to find involve providing a file or directory.
You could give it a try:
import org.springframework.context.support.GenericXmlApplicationContext;
String xmlDef = "...";
ApplicationContext ctx = new GenericXmlApplicationContext(new InputStreamResource(new ByteArrayInputStream(xmlDef.getBytes("UTF-8"))))
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 want to use anti-samy from OWASP.
They got a Policy object, that is instantiated via a factory method.
public static Policy getInstance(InputStream inputStream);
The InputStream that needs to be passed to the factory-method represents the config file for the policy-object.
Is it possible to use create a policy bean in an spring xml context config?
I know, that there is a Resource object, that can load files from classpath. But what I need is to make a InputStream out of that Resource object. Can I doe this directly in the xml-spring-context? Or do I need to write java code in order to get the InputStream?
Use the factory-method approach together with a constructor-arg (that will be mapped to a factory method argument) and automatically converted to an InputStream from a resource notation.
<bean id="policy" class="org.owasp.validator.html.Policy"
factory-method="getInstance">
<!-- type needed because there is also a getInstance(String) method -->
<constructor-arg
value="classpath:path/to/policyFile.xml"
type="java.io.InputStream" />
</bean>
See the following parts of the Spring Reference:
Instantiation with a static factory
method
Built-in Property Editors (InputStreamEditor is relevant here)
Examples of Dependency Injection (last section is about constructor-arg used in the context of a static factory-method)
#seanizer's solution would be a good one if Policy closed the InputStream after it was finished reading from it, but apparently it doesn't. This will result in a leak, the severity of which depends how often it is called, and the nature of the resource.
To be safe, you should consider writing a custom FactoryBean implementation instead, which handles the opening and closing of the InputStream safely. The FactoryBean would be injected with a Resource object.
I have been using spring for a while as my IOC. It has also a very nice way of injecting properties in your beans.
Now I am participating in a new project where the IOC is Guice. I dont understand fully the concept how should I inject properties in to my beans using Guice.
The question : Is it actually possible to inject raw properties ( strings , ints ) into my guice beans. If the answer is no , than maybe you know some nice Properties Framework for java.
Because right now I wanted to use the ResourceBundle class for simple properties management in my app. But after using spring for a while this just dont seem seriously enought for me.
this SO post discusses the use of various configuration frameworks, also the use of properties. I'm not sure it's to the point exactly for your needs, but perhaps you can find something of value there.
Spring provides for injection of configuration information found in XML files. I don't want the people installing my software to have to edit XML files, so for the kind of configuration information more properly in a plain text file (such as path information), I've gone back to using java.util.Properties since it's easy to use and fits into Spring pretty well, if you use a ClassPathResource, which permits path-free location of the file itself (it just has to be in the classpath; I put mine at the root of WEB-INF/classes.
Here's a quick method that returns a populated Properties object:
/**
* Load the Properties based on the property file specified
* by <tt>filename</tt>, which must exist on the classpath
* (e.g., "myapp-config.properties").
*/
public Properties loadPropertiesFromClassPath( String filename )
throws IOException
{
Properties properties = new Properties();
if ( filename != null ) {
Resource rsrc = new ClassPathResource(filename);
log.info("loading properties from filename " + rsrc.getFilename() );
InputStream in = rsrc.getInputStream();
log.info( properties.size() + " properties prior to load" );
properties.load(in);
log.info( properties.size() + " properties after load" );
}
return properties;
}
The file itself uses the normal "name=value" plaintext format, but if you want to use Properties' XML format just change properties.load(InputStream) to properties.loadFromXML(InputStream).
Hope that's of some help.
Injecting properties in Guice is easy. After reading in some properties from a file or however, you bind them using Names.bindProperties(Binder,Properties). You can then inject them using, for example, #Named("some.port") int port.