Refreshing OSGI application-context - osgi

I'm developing OSGI application and I need to refresh my application-context.xml during runtime.
I wanted to use
String ctxPath = ....
ApplicationContext ctx = new FileSystemXmlApplicationContext(ctxPath);
((ConfigurableApplicationContext)ctx).refresh();
The problem is - I can't find a way to get path to my application-context.xml (its inside OSGI bundle). The only way I'm aware of, to get inside files of bundle, is to use
InputStream inputStream = new BufferedInputStream(this.getClass().getClassLoader().getResourceAsStream("/application-context.xml"));
But I can't get path from InputStream. Any ideas how to make this work?

The only way I can imagine is to get the InputStream (you could also you the Bundle.getEntry for that) and then save the the file somewhere you would know the path. I have seen this approach in several applications so I assume it is correct.

Related

spring boot app cannot load bundle properties files

I am building an app that mostly provide REST services, nothing fancy. since my data consumed by the app can have multiple languages I thought about using the bundle files.
I created 3 files, one with the default file name and another two with specific languages. The files created using intellij IDE I am using.
I followed this guide https://www.baeldung.com/java-resourcebundle however on each run I am getting:
MissingResourceException: Can't find bundle for base name tp_app_strings, locale en_US
I tried numerous articles but none of them seems to resolve the issue.
One fun fact is that if I am using the #Value("classpath:tp_app_strings.properties") on a 'Resource' field I am able to get a reference to that file, so it spring is able to find it.
Additional thing that I tried was to create a WEB-INF directory and place the files there (read it in some article) but still no positive affect
The project structure is quite straight forward:
Spring boot version 2.2 running tomcat.
Any suggeestions would be highly appriciated
You can load the .properties file to the application context using #PropertySource annotation instead using #Value to load the .properties file to a org.springframework.core.io.Resource instance.
The usage;
#Configuration
#PropertySource("classpath:tp_app_strings.properties")
public class DefaultProperties {
#Value("${property1.name}") // Access properties in the above file here using SpringEL.
private String prop1;
#Value("${property2.name}")
private String prop2;
}
You wouldn't need java.util.ResourceBundle access properties this way. Use different or same class to load other .properties files as well.
Update 1:
In order to have the functionality of java.util.ResourceBundle, you can't just use org.springframework.core.io.Resource class. This class or non of it sub-classes don't provide functions to access properties by its name java.util.ResourceBundle whatsoever.
However, if you want a functionality like java.util.ResourceBundle, you could implement something custom like this using org.springframework.core.io.Resource;
#Configuration
public class PropertyConfig {
#Value("classpath:tp_app_strings.properties")
private Resource defaultProperties;
#Bean("default-lang")
public java.util.Properties getDefaultProperties() throws IOException {
Properties props = new Properties();
props.load(defaultProperties.getInputStream());
return props;
}
}
Make sure to follow correct naming convention when define the property file as java.util.Properties#load(InputStream) expect that.
Now you can #Autowire and use this java.util.Properties bean wherever you want just like with java.util.ResourceBundle using java.util.Properties#getProperty(String) or its overloaded counterpart.
I think it's problem of you properties file naming convention. use underline "_" for specifying locale of file like
filename_[languageCode]_[regionCode]
[languageCode] and [regionCode] are two letters standard code that [regionCode] section is optional
about code abbrivation standard take a look on this question
in your case change file name to tp_app_strings_en_US.properties

Deploying BEAN in OSGi plugin

I am currently deploying my custom controls as OSGi plugins and I wanted to do the same thing with my beans. I have tried putting them into the OSGi plugin and it works fine but the only problem I have is the faces-config.
It seems it has to be called faces-config in the OSGi plugin to work but that means i can't use beans in the NSF anymore because it seems to ignore the local faces-config.
Is there a way to change the name of the faces-config in the OSGi plugin?
Something like FEATURE-faces-config.xml?
In the class in your plugin that extends AbstractXspLibrary, you can override "getFacesConfigFiles", which should return an array of strings representing paths within the plugin to additional files of any name to load as faces-config additions. For example:
#Override
public String[] getFacesConfigFiles() {
return new String[] {
"com/example/config/beans.xml"
};
}
Then you can put the config file in that path within your Java source folder (or another folder that is included in build.properties) and it will be loaded in addition to your app's normal faces-config, beans and all.
The NSFs are running as separate, distinct Java applications. The OSGi plugin is running in the OSGi layer, above all those distinct Java applications, as a single code base. Consequently, the faces-config is only at that level.
It's possible to load them dynamically, by using an ImplicitObjectFactory, loaded from an XspContributor. That's what is done in OpenNTF Domino API for e.g. userScope (which is a bean stored in applicationScope of an NSF). See org.openntf.domino.xsp.helpers.OpenntfDominoImplicitObjectFactory, which is referenced in OpenntfDominoXspContributor, loaded via the extension point of type "com.ibm.xsp.library.Contributor".
A few caveats:
You have no control over what happens if you try to register your bean with a name the developer also uses for a different variable in that scope.
Unless you add code to check if the library is enabled, as we do, you'll be adding the bean to every database on the server.
You still need to add the library to the NSF. Unless you also provide a component that those databases will all use, there's no way you can programmatically add it, as far as I know.
It might be easier to skip the bean approach and just add an instance of the Java class in beforePageLoad, page controller class, or however you're managing the backing to the relevant XPage (if viewScope) or application (if sessionScope / applicationScope).

Look up a dynamic property at run-time in Spring from PropertySourcesPlaceholderConfigurer?

Not sure of the best approach to this. We've created a jar that could be used by different projects. The other projects relying on this jar need to provide certain properties defined in one of their spring properties files. (Our jar shouldn't care what they name those property files.)
Using #Value("${some.prop}") works great for most properties, however we now have the requirement that the name of the property to look up is dynamic. For example:
int val = getSomeVal();
String propNeeded = foo.getProperty("foo."+val+".dynamic.prop");
Not sure what "foo" should be to get my access. I looked into injecting Environment, however from all my googling it looks like that will not load from an xml property-placeholder definition (even if defined as a bean def for PropertySourcesPlaceholderConfigurer.) You seem to have to use #PropertySource, yet my main config is an XML file so not sure how to get Environment to work. (I can't really go 'old skool' and look up the property file as a class path Resource either since I'm not aware of the name of the file the users defined.)
I don't mind making this particular Service class ApplicationContextAware, but if I did that how could I get access to the underlying PropertySourcesPlaceholderConfigurer ? which I would 'seem?' to need in order to get access to a property dynamically?
The other option is that I force users of the jar to declare a bean by a name that I can look up
<util:properties id="appProps" location="classpath:application.properties" />
And I then inject appProps as Properties and look up from there. I don't like this approach though since it forces the users of the library to name an file by a common id. I would think the best solution is to just get a handle in some way to the underlying PropertySourcesPlaceholderConfigurer in my service class... I'm just not sure how to do it?
Why doesn't Spring simply allow PropertySource to be defined some how via your XML config and then I could just inject Environment?
Thanks for any suggestions how to accomplish what I want.
You could have a ReloadableResourceBundleMessageSource declared to read from the same source as the PropertySourcesPlaceholderConfigurer. This way you could just #Autowire MessageSource (or make your bean implement MessageSourceAware) and use that to retrieve your properties.
Main reason for using ReloadableResourceBundleMessageSource is to retrieve I18N messages, so that would kind of hacky...

What is difference between class path , file system?

I know that:
ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
loads context definition from an XML file located in the classpath, treating context definitions as classpath resources.
ApplicationContext context = new FileSystemXmlApplicationContext("bean.xml");
loads context definition from an XML file in the filesystem.
XmlWebApplicationContext
loads context definition from an XML file contained within a web application.
But, what does it exactly mean??
Thanks :)
ClassPathXmlApplicationContext will read files from your classpath. They must be in classes folder of your web application or in a jar in your libfolder.
FileSystemXmlApplicationContext can access all your file system, for example c:/config/applicationContext.xml.
XmlWebApplicationContext certainly can access to files contained in your web application, but this is not the most important thing. It implements WebApplicationContext and this means that it will detect ServletContextAware beans, register custom scopes (request, session, ...) among other things.
FileSystemXmlApplicationContext- You need to provide complete full path of xml bean
ClassPathXmlApplicationContext - In this case you DONOT need to set full path, as long as classpath is set
I think above opinion may have something wrong, FileSystemXmlApplicationContext can not access your whole file system, what it can only scan is your whole project folder.In order to prove my conclusion i make a example, first using ClasspathXmlApplicationContext and everything is normal, the second time i move beans.xml file to my desktop folder, so there is no beans.xml file in the project hirachy, and change ClassPathXmlApplicationContext to FileSytemXmlApplicationContext and something goes wrong, error trace below:
INFO: Loading XML bean definitions from file [/Users/crabime/Development/IdeaProjects/springInterview/Users/crabime/Desktop/beans.xml]
Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from file [/Users/crabime/Development/IdeaProjects/springInterview/Users/crabime/Desktop/beans.xml]; nested exception is java.io.FileNotFoundException: Users/crabime/Desktop/beans.xml (No such file or directory)
So FileSystemXmlApplicationContext can only detect the current project all folder. For example you make a directory which named config under the project root directory, and you can change your Main Class code like below:
ApplicationContext atx = new FileSystemXmlApplicationContext("/config/beans.xml");
And everything will ok again. So if all like sinuhepop said i think there should something need to be changed.

Setting freemarker template from classpath

I have a web application that I need to manually obtain a Freemarker template - the template is obtained via a class in a library project, but the actual tpl file is contained in the web application classpath. So, there are 2 projects, one 'taac-backend-api' and another 'taac-web'; taac-backend-api has the code to grab the template, and process it, but taac-web is where the template is stores (specifically in: WEB-INF/classes/email/vendor.tpl) - I have tried everything from using springs classpath resource to using Freemarkers setClassForTemplateLoading method. I assume this would work:
freemarkerConfiguration = new Configuration();
freemarkerConfiguration.setClassForTemplateLoading(this.getClass(), "");
Template freemarkerTemplate = freemarkerConfiguration.getTemplate("/email/vendor.tpl");
yet, I always get a FileNotFoundException. Can someone explain the best way to obtain a template from the classpath?
Thanks.
this is what ended up working for me:
freemarkerConfiguration = new Configuration(Configuration.VERSION_2_3_28);
freemarkerConfiguration.setClassForTemplateLoading(this.getClass(), "/");
Template freemarkerTemplate = freemarkerConfiguration.getTemplate("email/vendor.tpl");
In 2017, the following is deprecated:
Configuration conf = new Configuration();
We should pass freemarker.template.Version to the constructor:
Configuration conf = new Configuration(new Version(2, 3, 23));
conf.setClassForTemplateLoading(Application.class, "/views");
where the version numbers refer to the current version of FreeMarker.
The views directory is located in src/main/resources.
freemarkerConfiguration = new Configuration();
freemarkerConfiguration.setClassForTemplateLoading(this.getClass(), "");
Template freemarkerTemplate = freemarkerConfiguration.getTemplate("template.tpl");
Use this method to load the classes from the package where your class is located, so if your class is
org.foo.SomeClass the templates will be looked for in /org/foo in the classpath. This keeps your templates stored with the class that uses/loads them.
If you are using Struts 2 and the Conventions plugin, wuntee's solution doesn't seem to work: setClassForTemplateLoading in turn creates an instance of ClassTemplateLoader which doesn't find files in jars no matter what path prefix is specified.
Instead, create an instance of StrutsClassTemplateLoader. (I do this in a custom sub-class of FreemarkerManager in its getTemplateLoader method.) It takes no parameters, so presumably it just knows how Struts and Conventions do things.
Use the following config and place it in application properties.
spring.freemarker.template-loader-path=

Resources