Application/Deployment specific properties with JAX-RS and WildFly - java

I am used to Spring on Tomcat/Jetty and I now work on an existing JAX-RS project running on WildFly (RESTEasy).
I would like to know where do the application/deployment property files go on WildFly, standalone/configuration/myapp.properties?
Then how does the application load them? I tried in our class extending javax.ws.rs.core.Application:
#javax.ws.rs.ApplicationPath("")
public class ApplicationConfig extends Application {
#Override
public Map<String, Object> getProperties() {
System.out.println(">>>>>>>>>>>>>>>> get properties");
// I added this method but nothing is printed...
}
#Override
public Set<Class<?>> getClasses() {
System.out.println(">>>>>>>>>>>>>>>> get classes");
// This is printed
...
// classes are loaded correctly
}
}
Then how would I access the properties in the controllers? By the way we don't use dependency injection.
Thanks!

Some Investigation...
Normally what should work
The getProperties() should be called on startup to load any required application properties.
You should be able to inject javax.ws.rs.core.Configuration into your resource classes (with #Context) and retrieve properties through that object. This is stated in the javadoc
This interface can be injected using the Context annotation.
Test
#ApplicationPath("/api")
public class RestApplication extends Application {
#Override
public Map<String, Object> getProperties() {
System.out.println(">>>>>>>>>>>>>>>> get properties");
Map<String, Object> props = new HashMap<>();
props.put("message", "Hello Configuration Properties!");
return props;
}
}
#Path("config")
public class ConfigResource {
#Context
private Configuration configuration;
#GET
public Response getProperty(#QueryParam("prop") String prop) {
String propValue = (String)configuration.getProperty(prop);
return Response.ok(propValue).build();
}
}
Discoveries
The above doesn't work from what I tested with Resteasy 3.0.9.Final. I get some error about no context for this type. I don't know why. Might be a bug, I don't know. Maybe something you can look into.
The above works fine with Jersey 2.16
What works with Resteasy
What I could get to work with Resteasy is to inject Application (as mentioned here into the resource (also with #Context) and get the properties that way.
#Path("config")
public class ConfigResource {
#Context
Application application;
#GET
public Response getProperty(#QueryParam("prop") String prop) {
String propValue = (String)application.getProperties().get(prop);
return Response.ok(propValue).build();
}
}

Related

Can I manually load #ConfigurationProperties without the Spring AppContext?

Is there any way to load a class marked with #ConfigurationProperties without using a Spring Context directly? Basically I want to reuse all the smart logic that Spring does but for a bean I manually instantiate outside of the Spring lifecycle.
I have a bean that loads happily in Spring (Boot) and I can inject this into my other Service beans:
#ConfigurationProperties(prefix="my")
public class MySettings {
String property1;
File property2;
}
See the spring docco for more info http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-external-config-command-line-args
But now I need to access this bean from a class that is created outside of Spring (by Hibernate). The class is created so early in the app startup process that Spring Boot has not yet made the application context available through the classic lookup helper methods or roll-my-own static references.
So I instead want to do something like:
MySettings mySettings = new MySettings();
SpringPropertyLoadingMagicClass loader = new SpringPropertyLoadingMagicClass();
loader.populatePropertyValues(mySettings);
And have MySettings end up with all its values loaded, from the command line, system properties, app.properties, etc. Is there some class in Spring that does something like this or is it all too interwoven with the application context?
Obviously I could just load the Properties file myself, but I really want to keep Spring Boot's logic around using command line variables (e.g. --my.property1=xxx), or system variables, or application.properties or even a yaml file, as well as its logic around relaxed binding and type conversion (e.g. property2 is a File) so that it all works exactly the same as when used in the Spring context.
Possible or pipe dream?
Thanks for your help!
I had the same "issue".
Here is how I solved it in SpringBoot version 1.3.xxx and 1.4.1.
Let's say we have the following yaml configuration file:
foo:
apis:
-
name: Happy Api
path: /happyApi.json?v=bar
-
name: Grumpy Api
path: /grumpyApi.json?v=grrr
and we have the following ConfigurationProperties:
#ConfigurationProperties(prefix = "foo")
public class ApisProperties {
private List<ApiPath> apis = Lists.newArrayList();
public ApisProperties() {
}
public List<ApiPath> getApis() {
return apis;
}
public static class ApiPath {
private String name;
private String path;
public String getName() {
return name;
}
public void setName(final String aName) {
name = aName;
}
public String getPath() {
return path;
}
public void setPath(final String aPath) {
path = aPath;
}
}
}
Then, to do the "magic" things of Spring Boot programmatically (e.g. loading some properties in a static method), you can do:
private static ApisProperties apiProperties() {
try {
ClassPathResource resource;
resource = new ClassPathResource("/config/application.yml");
YamlPropertiesFactoryBean factoryBean;
factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setSingleton(true); // optional depends on your use-case
factoryBean.setResources(resource);
Properties properties;
properties = factoryBean.getObject();
MutablePropertySources propertySources;
propertySources = new MutablePropertySources();
propertySources.addLast(new PropertiesPropertySource("apis", properties));
ApisProperties apisProperties;
apisProperties = new ApisProperties();
PropertiesConfigurationFactory<ApisProperties> configurationFactory;
configurationFactory = new PropertiesConfigurationFactory<>(apisProperties);
configurationFactory.setPropertySources(propertySources);
configurationFactory.setTargetName("foo"); // it's the same prefix as the one defined in the #ConfigurationProperties
configurationFactory.bindPropertiesToTarget();
return apisProperties; // apiProperties are fed with the values defined in the application.yaml
} catch (BindException e) {
throw new IllegalArgumentException(e);
}
}
Here's an update to ctranxuan's answer for Spring Boot 2.x. In our situation, we avoid spinning up a Spring context for unit tests, but do like to test our configuration classes (which is called AppConfig in this example, and its settings are prefixed by app):
public class AppConfigTest {
private static AppConfig config;
#BeforeClass
public static void init() {
YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setResources(new ClassPathResource("application.yaml"));
Properties properties = factoryBean.getObject();
ConfigurationPropertySource propertySource = new MapConfigurationPropertySource(properties);
Binder binder = new Binder(propertySource);
config = binder.bind("app", AppConfig.class).get(); // same prefix as #ConfigurationProperties
}
}
The "magic" class you are looking for is PropertiesConfigurationFactory. But I would question your need for it - if you only need to bind once, then Spring should be able to do it for you, and if you have lifecycle issues it would be better to address those (in case they break something else).
This post is going into similar direction but extends the last answer with also validation and property placeholder resolutions.
Spring Boot Binder API support for #Value Annotations
#Value annotations in ConfigurationPropertys don't seem to bind properly though (at least if the referenced values are not part of the ConfigurationProperty's prefix namespace).
import org.springframework.boot.context.properties.bind.Binder
val binder = Binder.get(environment)
binder.bind(prefix, MySettings.class).get

Scope of Singletons

I'm using a singleton bean to provide configuration values stored in a database to my Java EE application.
#Singleton
public class ConfigurationProvider {
private final Map<String, ConfigurationEntity> configMap = new HashMap<>();
#PersistenceContext(unitName = DatabaseConstants.PERSISTENCE_UNIT)
private EntityManager em;
public String getConfiguration(String key) {
if (configMap.containsKey(key)) {
return configMap.get(key).getValue();
}
ConfigurationEntity config = em.find(ConfigurationEntity.class, key);
em.detach(config);
if (config == null) {
throw new RuntimeException("Configuration not found for " + key);
}
configMap.put(key, config);
return config.getValue();
}
public void clear() {
configMap.clear();
}
public Collection<ConfigurationEntity> getCurrentConfigurationState() {
return configMap.values();
}
}
A Producer let me inject the values
public class ConfigurationProducer {
#Inject
private ConfigurationProvider configProvider;
#Produces
#ConfigurationValue
public String getConfiguration(InjectionPoint ip) {
String key = createKey(ip);
return configProvider.getConfiguration(key);
}
Here an example
#Inject
#ConfigurationValue
private Instance<String> endpoint;
This loads the endpont from the database. For testing reasons, the value should be changeable.
So what you saw is part of an ejb module.
To reload the values, I created a REST-Interface that provides the functionality. This REST-Service is part of an additional WAR packaged together with the ejb module in one ear file.
#Path("/configuration")
public class ConfigurationResource {
#EJB
private ConfigurationProvider configurationProvider;
#GET
#Path("/current")
#Produces({ "application/json" })
public Collection<ConfigurationEntity> getCurrentConfiguration() {
return configurationProvider.getCurrentConfigurationState();
}
}
But the problem is, that the war has it's own instance of the configuration provider. So I cannot reaload the 'cache'.
Why I have two instance of my singleton in the same ear?
I don't think you can use the ConfigurationProvider EJB that way. That EJB would need to have a remote interface, and you would access it as any remote EJB from the external WAR. The external WAR has a different class loader, hence it will not find EAR singleton EJBs.
It seems that you are using both CDI's #Inject and EJB's #EJB to inject your ConfigurationProvider instance. Considering that you are not synchronizing access to map and that you are using EntityManager, which is not thread safe, you propably should be using #EJB.
That said, you need just a minor change in your code:
public class ConfigurationProducer {
#EJB
private ConfigurationProvider configProvider;
#Produces
#ConfigurationValue
public String getConfiguration(InjectionPoint ip) {
String key = createKey(ip);
return configProvider.getConfiguration(key);
}
Solution:
Maven packaged the ejb module a second time in the war's lib folder. I had to set the scope in the pom.xml to provided. With ejb modules it works without any exclutions but for the war you have to do it manually.
Now it works.

How to properly access application state from JAX-RS

#Path("/test")
public class MyClass {
#GET
public Response response() {
// Generating some expensive object here.
}
Right now I load the data into arrays etc inside the "response" function, but I want to do it before the query is even made. This way, I want to avoid reloading the data every time a a query is made. How do I achieve this?
This depends on your framework. Are you using the reference implementation Jersey? Jersey comes bundled with HK2 automatically. Then you could add
#Path("/test")
public class MyClass {
#Inject
private MyState stateful;
// here comes your code...
}
to your resource. Of course, you would still need to configure what to inject into this field. With HK2, you use an AbstractBinder for this purpose:
class MyBinder extends AbstractBinder {
private final MyState stateful;
public MyBinder (MyState stateful) {
this.stateful = stateful;
}
#Override
protected void configure() {
bind(stateful).to(MyState.class);
}
}
Finally, you need to add this binder on the application's setup. For this purpose, JAX-RS Application object can be queried for singletons. Simply add the required instance to the application such that it is returned by Application#getSingletons as here:
class MyJaxRSApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
return Collections.singletonSet(MyClass.class);
}
#Override
public Set<Object> getSingletons() {
return Collections.singletonSet(new MyBinder(new MyStateImpl()));
}
}
You can now run your application where MyStateImpl is always injected into MyClass.

How can I select Spring bean instance at runtime

Based on parameters passed to a method, I need to select from one of many Spring beans that are implementations of the same class, but configured with different parameters.
E.g. if user A invokes the method, I need to call dooFoo() on bean A, but if it's user B then I need to call the very same method, only on bean B.
Is there a 'Springier' way of doing this other than sticking all the beans in a map, and deriving a key from the parameters passed to my method?
We face that issue in our project, and we solve it through a Factory-Like class. The client class -the one that needed the bean at runtime- had an instance of the factory, that was injected through Spring:
#Component
public class ImTheClient{
#Autowired
private ImTheFactory factory;
public void doSomething(
Parameters parameters) throws Exception{
IWantThis theInstance = factory.getInstance(parameters);
}
}
So, the IWantThis instance depends on the runtime value of the parameters parameter. The Factory implementation goes like this:
#Component
public class ImTheFactoryImpl implements
ImTheFactory {
#Autowired
private IWantThisBadly anInstance;
#Autowired
private IAlsoWantThis anotherInstance;
#Override
public IWantThis getInstance(Parameters parameters) {
if (parameters.equals(Parameters.THIS)) {
return anInstance;
}
if (parameters.equals(Parameters.THAT)) {
return anotherInstance;
}
return null;
}
}
So, the factory instance holds reference to both of the posible values of the IWantThis class, being IWantThisBadly and IAlsoWantThis both implementations of IWantThis.
Seems like do you want a ServiceLocator using the application context as registry.
See ServiceLocatorFactoryBean support class for creating ServiceLocators mapping keys to bean names without coupling client code to Spring.
Other option is to use a naming convention or annotation based configuration.
for example, assuming that you annotate Services with #ExampleAnnotation("someId"), you can use something like the following Service Locator to retrieve them.
public class AnnotationServiceLocator implements ServiceLocator {
#Autowired
private ApplicationContext context;
private Map<String, Service> services;
public Service getService(String id) {
checkServices();
return services.get(id);
}
private void checkServices() {
if (services == null) {
services = new HashMap<String, Service>();
Map<String, Object> beans = context.getBeansWithAnnotation(ExampleAnnotation.class);
for (Object bean : beans.values()) {
ExampleAnnotation ann = bean.getClass().getAnnotation(ExampleAnnotation.class);
services.put(ann.value(), (Service) bean);
}
}
}
}
Sticking them in a map sounds fine. If it's a Spring-managed map (using util:map, or in Java config), that's better than creating it somewhere else, because then Spring owns all the object references and can manage their lifecycle properly.
If the beans (A, B) you are talking about are SessionScope its no problem at all, they will be selected correctly.
public class BusinessLogic {
private BaseClassOfBeanAandB bean;
public void methodCalledByUserAorB() {
bean.doFoo();
}
}

Use ContainerRequestFilter in Jersey without web.xml

I am trying to intercept requests in Jersey running inside Glassfish.
I created an implementation of ContainerRequestFilter
package mycustom.api.rest.security;
#Provider
public class SecurityProvider implements ContainerRequestFilter {
#Override
public ContainerRequest filter(ContainerRequest request) {
return request;
}
}
My app is started using a subclass of PackagesResourceConfig.
When Glassfish starts, Jerseys find my provider:
INFO: Provider classes found:
class mycustom.rest.security.SecurityProvider
But it never hits that filter method. What am I missing??
Everything else seems to be working fine. I added a couple of ContextResolver providers to do JSON mapping and they work fine. Requests hit my resources fine, it just never goes through the filter.
I don't think container filters are loaded in as providers. I think you have to set the response filters property. Strangely PackagesResourceConfig doesn't have a setProperty() but you could overload getProperty() and getProperties():
public Object getProperty(String propertyName) {
if(propertyName.equals(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS)) {
return new String[] {"mycustom.rest.security.SecurityProvider"};
} else {
return super.getProperty(propertyName);
}
}
public Map<String,Object> getProperties() {
propName = ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS;
Map<String,Object> result = super.getProperties();
result.put(propName,getProperty(propName));
return result;
}
Actually, reading the javadocs more closely, it appears the preferred method is:
myConfig.getProperties().put(ResourceConfig.PROPERTY_CONTAINER_REQUEST_FILTERS,
new String [] {"mycustom.rest.security.SecurityProvider"});

Categories

Resources