How to keep properties values in session scope? - java

I am using spring. i have an externalized properties file. i am loading it as below.
<context:property-placeholder location="file:///C:/some.properties"/>
Now how can i keep properties in session as key-value pair ?
i tried writing a listener which extends ServletContextListener.
public class Sample implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
//here i tried to get the values of properties file as below.
InputStream stream = event.getServletContext().getResourceAsStream("C:\\some.properties");
//But here stream is coming as null
}
}
Am i missing anything here?
Thanks!

SetvletContext's contextInitlalized() is called when the servlet context is initialized when the application loads successfully,
If you want to store it properties file in application context you can put it in
event.getServletContext().setAttribute("global_properties", propertiesInstance);
If you want it on per session, then you need to hook it into HttpSessionListener's sessionCreated() method
So put the data that is frequently used and that is shared across the application in applicationscope and the data that is limited to a session but frequently used put it in session

I would suggest use PropertyPlaceHolderConfigurer which communicates with a ServletContextListner. This class PropertyPlaceHolderConfigurer has one method call processProperties in which you could get the map of all the properties.
#Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
Properties props) throws BeansException {
super.processProperties(beanFactoryToProcess, props);
resolvedProps = new HashMap<String, String>();
for (Object key : props.keySet()) {
String keyStr = key.toString();
resolvedProps.put(keyStr, parseStringValue(props.getProperty(keyStr), props,
new HashSet()));
}
}
And in the listner contextInitialized() you could do like:
ServletContext servletContext = sce.getServletContext();
WebApplicationContext context = WebApplicationContextUtils
.getRequiredWebApplicationContext(servletContext);
ExposablePropertyPlaceHolder configurer =(ExposablePropertyPlaceHolder)context.getBean(propertiesBeanName);
sce.getServletContext().setAttribute(contextProperty, configurer.getResolvedProps());
where ExposablePropertyPlaceHolder is the class which extends PropertyPlaceHolderConfigurer.

Related

how to get session object from resource resolver factory?

#Component
public class AddNode {
#Reference
static ResourceResolverFactory resolverFactory;
static Session session;
public static void main(String[] args) throws Exception {
ResourceResolver resourceResolver = resolverFactory.getServiceResourceResolver(null);
session = resourceResolver.adaptTo(Session.class);
Node root = session.getRootNode();
Node adobe = root.addNode("ProgramNode");
Node day = adobe.addNode("subnode");
day.setProperty("jcr:title", "programNode");
Node node = root.getNode("ProgramNode/subnode");
System.out.println(node.getPath());
System.out.println(node.getProperty("jcr:title").getString());
session.save();
session.logout();
}
}
here resolverFactory.getServiceResourceResolver(null); i need to pass any parameter at null.please give some example to get access of repository.
We should use System Users to access repository when accessing via Factory. You would need to
Create system user, provide appropriate permissions
Map bundle symbolic name to system user
Use system user to get session via ResourceResolverFactory
Map<String, Object> param = new HashMap<String, Object>();
param.put(ResourceResolverFactory.SUBSERVICE, "<sub-service-name>");
ResourceResolver resolver = null;
resolver = resolverFactory.getServiceResourceResolver(param);
session = resolver.adaptTo(Session.class);
You can refer this helpx link article also to get idea about using Service Users to create session
Have a look at the documentation of ResourceResolverFactory.
https://sling.apache.org/apidocs/sling7/org/apache/sling/api/resource/ResourceResolverFactory.html

How to get ResourceResolver in a background thread?

I'm working on a solution in Adobe Experience Manager (AEM) that receives an HTTP request containing a URL to a file, which I want to download and store in the JCR.
So, I have a servlet that receives the request. It spawns a thread so that I can do the download in the background, and then redirects to a confirmation page. This allows me to send the user on their way without waiting while I try to download the file.
I can download the file just fine, but I'm having trouble getting a usable ResourceResolver to store the file in the JCR from my thread.
At first, I simply referenced the request's ResourceResolver in the background thread:
Servlet:
public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response)
throws ServletException, IOException {
...
signingProvider.getDocFromService(params, request.getResourceResolver());
response.sendRedirect(confirmationPage);
}
And in the provider class:
public void getDocFromService(Map<String, String> params, ResourceResolver resolver) {
new Thread( new Runnable() {
public void run() {
Session session = null;
if (resolver != null) {
session = resolver.adaptTo(Session.class);
Node root = session.getRootNode();
...
}
}
}
}
but that didn't work. After reading up on resolvers vs threads, I thought I would be better off creating a new Resolver instance, so I tried to inject a ResourceResolverFactory:
Servlet:
signingProvider.getDocFromService(params);
Provider:
public void getDocFromService(Map<String, String> params) {
new Thread( new Runnable() {
#Reference
private ResourceResolverFactory resolverFactory;
// security hole, fix later
ResourceResolver resolver = resolverFactory.getAdministrativeResourceResolver(null);
Session session = null;
if (resolver != null) {
session = resolver.adaptTo(Session.class);
Node root = session.getRootNode();
...
}
}
}
but the ResourceResolverFactory is null, so I crash when asking it for a resolver. Apparently, no factory is getting injected into the #Reference
I would really rather not do the work on the main thread; after I download the file I'm going to turn around and read it from the JCR and copy it elsewhere. Both of these operations could be slow or fail. I have a copy of the file at the original URL, so the end-user needn't care if my download/uploads had trouble. I just want to send them a confirmation so they can get on with business.
Any advice on how to get a ResourceResolver in a separate thread?
For things like post\background processing you can use Sling Jobs. Please refer to the documentation to find out some details.
Note: #daniil-stelmakh brings a good point in his answer, sling jobs are much better suited for your purpose, to add to his answer, here is a sling tutorial that demonstrates sling jobs: https://sling.apache.org/documentation/tutorials-how-tos/how-to-manage-events-in-sling.html
To answer your question directly:
The issue, really is the placement of #Reference annotation.
That annotation is handled by Maven SCR Plugin and it should be placed on a private member of a '#Component' annotated class.
Basically move your ResourceResolverFactory declaration to become a private member of your class, not the Thread.
#Component(
label = "sample service",
description = "sample service"
)
#Service
public class ServiceImpl {
#Reference
private ResourceResolverFactory resolverFactory;
public void getDocFromService(Map<String, String> params) {
new Thread( new Runnable() {
// security hole, fix later
ResourceResolver resolver = resolverFactory.getAdministrativeResourceResolver(null);
Session session = null;
if (resolver != null) {
session = resolver.adaptTo(Session.class);
Node root = session.getRootNode();
...
}
}
}
}

Loading Property file from FileSystem in a Java EE application

I am using EJB 3.0 and CDI to develop a java ee application which will be deployed in Websphere application server.
I have a requirement to have a property file from which i read certain configuration parameters and this property file should reside in the filesystem of the host system where my code will be deployed.
The base path ( directory where the property file will be placed ) for the property file is configured as a Name space binding String resource in Websphere application server.
Currently i have coded a Utility class to retrieve and use the property file which looks as below.
#Singleton
public class AppPropertyUtil {
private static Hashtable apppProperties;
#Resource(name="jndi/basePath",lookup="jndi/basePath")
private static String basePath;
private static final Logger LOGGER = LoggerFactory.getLogger(AppPropertyUtil.class);
protected void loadPropertyBundleFromFileSystem(String path)
{
InputStream inputStream = null;
Properties properties = null;
try {String fullPath=basePath+"/"+path+".properties";
LOGGER.info("Property file path : "+fullPath);
inputStream = new FileInputStream(new File(fullPath));
if (inputStream != null) {
properties = new Properties();
properties.load(inputStream);
LOGGER.info("Properties loaded");
apppProperties = (Hashtable)properties;
}
} catch (FileNotFoundException exception) {
LOGGER.error("Cannot read property bundle ",exception);
}
catch (IOException | IllegalArgumentException exception) {
LOGGER.error("Unable to loadproperties ",
exception);
}
}
public String getProperty(String key)
{
if(apppProperties == null)
{
loadPropertyBundleFromFileSystem("AppProps");
}
Object value = apppProperties.get(key);
if(value != null){
return (String) value;
}
return null;
}
}
But having the #Resource annotation will mandate that the AppPropertyUtil class be injected inside any class that wishes to use it. So, I will not be able to use this in any POJO classes which are not managed.
Please help me understand whether this is the best approach to go with for the above requirement or could this be improved. I would also like to make the getProperty method and loadPropertyBundleFromFileSystem method static to have it being used from a static context, but it is not possible as the class should be injected to be used.
Thanks in advance
Ideally, you shouldn't need to access to your class from any POJO. Use design patterns and separate your concerns. Use your class as a service to load up your property file and serve out a Map or Set of it's contents. POJO Models shouldn't care, but your other classes can read that map/set of properties and pass them to your pojo's or other classes which need it but don't have direct access to it.
The other option is to remove the #Resource and make this just a plain old util class that you pass in the filename to read in it's constructor, then when you call getProperty, you do what you do to check to see if it's been loaded already, if not, load it.
Wrap that in a factory that supplies the #Resource parts and it keeps your EE code from bleeding into your other jars (utils).

Updating Dropwizard config at runtime

Is it possible to have my app update the config settings at runtime? I can easily expose the settings I want in my UI but is there a way to allow the user to update settings and make them permanent ie save them to the config.yaml file? The only way I can see it to update the file by hand then restart the server which seems a bit limiting.
Yes. It is possible to reload the service classes at runtime.
Dropwizard by itself does not have the way to reload the app, but jersey has.
Jersey uses a container object internally to maintain the running application. Dropwizard uses the ServletContainer class of Jersey to run the application.
How to reload the app without restarting it -
Get a handle to the container used internally by jersey
You can do this by registering a AbstractContainerLifeCycleListener in Dropwizard Environment before starting the app. and implement its onStartup method as below -
In your main method where you start the app -
//getting the container instance
environment.jersey().register(new AbstractContainerLifecycleListener() {
#Override
public void onStartup(Container container) {
//initializing container - which will be used to reload the app
_container = container;
}
});
Add a method to your app to reload the app. It will take in the list of string which are the names of the service classes you want to reload. This method will call the reload method of the container with the new custom DropWizardConfiguration instance.
In your Application class
public static synchronized void reloadApp(List<String> reloadClasses) {
DropwizardResourceConfig dropwizardResourceConfig = new DropwizardResourceConfig();
for (String className : reloadClasses) {
try {
Class<?> serviceClass = Class.forName(className);
dropwizardResourceConfig.registerClasses(serviceClass);
System.out.printf(" + loaded class %s.\n", className);
} catch (ClassNotFoundException ex) {
System.out.printf(" ! class %s not found.\n", className);
}
}
_container.reload(dropwizardResourceConfig);
}
For more details see the example documentation of jersey - jersey example for reload
Consider going through the code and documentation of following files in Dropwizard/Jersey for a better understanding -
Container.java
ContainerLifeCycleListener.java
ServletContainer.java
AbstractContainerLifeCycleListener.java
DropWizardResourceConfig.java
ResourceConfig.java
No.
Yaml file is parsed at startup and given to the application as Configuration object once and for all. I believe you can change the file after that but it wouldn't affect your application until you restart it.
Possible follow up question: Can one restart the service programmatically?
AFAIK, no. I've researched and read the code somewhat for that but couldn't find a way to do that yet. If there is, I'd love to hear that :).
I made a task that reloads the main yaml file (it would be useful if something in the file changes). However, it is not reloading the environment. After researching this, Dropwizard uses a lot of final variables and it's quite hard to reload these on the go, without restarting the app.
class ReloadYAMLTask extends Task {
private String yamlFileName;
ReloadYAMLTask(String yamlFileName) {
super("reloadYaml");
this.yamlFileName = yamlFileName;
}
#Override
public void execute(ImmutableMultimap<String, String> parameters, PrintWriter output) throws Exception {
if (yamlFileName != null) {
ConfigurationFactoryFactory configurationFactoryFactory = new DefaultConfigurationFactoryFactory<ReportingServiceConfiguration>();
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
ObjectMapper objectMapper = Jackson.newObjectMapper();
final ConfigurationFactory<ServiceConfiguration> configurationFactory = configurationFactoryFactory.create(ServiceConfiguration.class, validator, objectMapper, "dw");
File confFile = new File(yamlFileName);
configurationFactory.build(new File(confFile.toURI()));
}
}
}
You can change the configuration in the YAML and read it while your application is running. This will not however restart the server or change any server configurations. You will be able to read any changed custom configurations and use them. For example, you can change the logging level at runtime or reload other custom settings.
My solution -
Define a custom server command. You should use this command to start your application instead of the "server" command.
ArgsServerCommand.java
public class ArgsServerCommand<WC extends WebConfiguration> extends EnvironmentCommand<WC> {
private static final Logger LOGGER = LoggerFactory.getLogger(ArgsServerCommand.class);
private final Class<WC> configurationClass;
private Namespace _namespace;
public static String COMMAND_NAME = "args-server";
public ArgsServerCommand(Application<WC> application) {
super(application, "args-server", "Runs the Dropwizard application as an HTTP server specific to my settings");
this.configurationClass = application.getConfigurationClass();
}
/*
* Since we don't subclass ServerCommand, we need a concrete reference to the configuration
* class.
*/
#Override
protected Class<WC> getConfigurationClass() {
return configurationClass;
}
public Namespace getNamespace() {
return _namespace;
}
#Override
protected void run(Environment environment, Namespace namespace, WC configuration) throws Exception {
_namespace = namespace;
final Server server = configuration.getServerFactory().build(environment);
try {
server.addLifeCycleListener(new LifeCycleListener());
cleanupAsynchronously();
server.start();
} catch (Exception e) {
LOGGER.error("Unable to start server, shutting down", e);
server.stop();
cleanup();
throw e;
}
}
private class LifeCycleListener extends AbstractLifeCycle.AbstractLifeCycleListener {
#Override
public void lifeCycleStopped(LifeCycle event) {
cleanup();
}
}
}
Method to reload in your Application -
_ymlFilePath = null; //class variable
public static boolean reloadConfiguration() throws IOException, ConfigurationException {
boolean reloaded = false;
if (_ymlFilePath == null) {
List<Command> commands = _configurationBootstrap.getCommands();
for (Command command : commands) {
String commandName = command.getName();
if (commandName.equals(ArgsServerCommand.COMMAND_NAME)) {
Namespace namespace = ((ArgsServerCommand) command).getNamespace();
if (namespace != null) {
_ymlFilePath = namespace.getString("file");
}
}
}
}
ConfigurationFactoryFactory configurationFactoryFactory = _configurationBootstrap.getConfigurationFactoryFactory();
ValidatorFactory validatorFactory = _configurationBootstrap.getValidatorFactory();
Validator validator = validatorFactory.getValidator();
ObjectMapper objectMapper = _configurationBootstrap.getObjectMapper();
ConfigurationSourceProvider provider = _configurationBootstrap.getConfigurationSourceProvider();
final ConfigurationFactory<CustomWebConfiguration> configurationFactory = configurationFactoryFactory.create(CustomWebConfiguration.class, validator, objectMapper, "dw");
if (_ymlFilePath != null) {
// Refresh logging level.
CustomWebConfiguration webConfiguration = configurationFactory.build(provider, _ymlFilePath);
LoggingFactory loggingFactory = webConfiguration.getLoggingFactory();
loggingFactory.configure(_configurationBootstrap.getMetricRegistry(), _configurationBootstrap.getApplication().getName());
// Get my defined custom settings
CustomSettings customSettings = webConfiguration.getCustomSettings();
reloaded = true;
}
return reloaded;
}
Although this feature isn't supported out of the box by dropwizard, you're able to accomplish this fairly easy with the tools they give you.
Before I get started, note that this isn't a complete solution for the question asked as it doesn't persist the updated config values to the config.yml. However, this would be easy enough to implement yourself simply by writing to the config file from the application. If anyone would like to write this implementation feel free to open a PR on the example project I've linked below.
Code
Start off with a minimal config:
config.yml
myConfigValue: "hello"
And it's corresponding configuration file:
ExampleConfiguration.java
public class ExampleConfiguration extends Configuration {
private String myConfigValue;
public String getMyConfigValue() {
return myConfigValue;
}
public void setMyConfigValue(String value) {
myConfigValue = value;
}
}
Then create a task which updates the config:
UpdateConfigTask.java
public class UpdateConfigTask extends Task {
ExampleConfiguration config;
public UpdateConfigTask(ExampleConfiguration config) {
super("updateconfig");
this.config = config;
}
#Override
public void execute(Map<String, List<String>> parameters, PrintWriter output) {
config.setMyConfigValue("goodbye");
}
}
Also for demonstration purposes, create a resource which allows you to get the config value:
ConfigResource.java
#Path("/config")
public class ConfigResource {
private final ExampleConfiguration config;
public ConfigResource(ExampleConfiguration config) {
this.config = config;
}
#GET
public Response handleGet() {
return Response.ok().entity(config.getMyConfigValue()).build();
}
}
Finally wire everything up in your application:
ExampleApplication.java (exerpt)
environment.jersey().register(new ConfigResource(configuration));
environment.admin().addTask(new UpdateConfigTask(configuration));
Usage
Start up the application then run:
$ curl 'http://localhost:8080/config'
hello
$ curl -X POST 'http://localhost:8081/tasks/updateconfig'
$ curl 'http://localhost:8080/config'
goodbye
How it works
This works simply by passing the same reference to the constructor of ConfigResource.java and UpdateConfigTask.java. If you aren't familiar with the concept see here:
Is Java "pass-by-reference" or "pass-by-value"?
The linked classes above are to a project I've created which demonstrates this as a complete solution. Here's a link to the project:
scottg489/dropwizard-runtime-config-example
Footnote: I haven't verified this works with the built in configuration. However, the dropwizard Configuration class which you need to extend for your own configuration does have various "setters" for internal configuration, but it may not be safe to update those outside of run().
Disclaimer: The project I've linked here was created by me.

Reading .properties file only once while web app deployment

Having a Java web application, how does one read properties file only once when the app is deployed (storing them later in some singleton)? Meaning, configuration changes would require redeployment.
Otherwise, is there an alternative way to prevent an app from constantly reading .properties file? Previously I had settings in my web.xml file, but now .properties is required.
Code used to read app settings from JBoss configuration path:
File f = new File(System.getProperty("jboss.server.config.dir"),
"myappsettings.properties");
Properties p = new Properties();
p.load(new FileInputStream(f));
try {
db_name = p.getProperty("DATABASE_NAME"));
file_path = p.getProperty("FILE_PATH"));
...
} catch (Exception e) {
...
}
Starting with JEE6, another alternative to the ServletContextListener could be using a singleton startup bean:
#Singleton
#Startup
public class PropertyRegistry {
#PostConstruct
private void init(){
//do something
}
}
Implement your own ServletContextListener:
public class PropertiesReadingListener implements ServletContextListener {
#Override
public void contextInitialized(ServletContextEvent event) {
//read properties here...
event.
getServletContext().
setAttribute("db_name", p.getProperty("DATABASE_NAME"));
}
#Override
public void contextDestroyed(ServletContextEvent event) {
}
}
You must reference this class in web.xml:
<listener>
<listener-class>com.example.PropertiesReadingListener</listener-class>
</listener>

Categories

Resources