How to get handle to a resource within a JSF class? - jsf

I've put a properties file within src/main/resources in my JSF project.
How do I get a handle to it? I understand that EL doesn't work within a backing bean.
Note: The file is in src/main/resources - NOT src/main/webapps/resources, so the following doesn't work:
FacesContext context = FacesContext.getCurrentInstance();
File value = context.getApplication().evaluateExpressionGet(context, "#{resource['resources:email.properties']}", File.class);

It's thus in the classpath. You can just use ClassLoader#getResourceAsStream() to get an InputStream out of it. Assuming that src is the classpath root and that main/resources is the package:
InputStream input = Thread.currentThread().getContextClassLoader().getResourceAsStream("main/resources/foo.properties");
Properties properties = new Properties();
properties.load(input);
// ...
Alternatively, if it's supposed to be specific to the webapp and thus isn't supposed to be overrideable by a file on the same path elsewhere in the classpath which has a higher classloading precedence (e.g. in appserver's lib or the JRE's lib), then use ExternalContext#getResourceAsStream() instead.
InputStream input = FacesContext.getCurrentInstance().getExternalContext().getResourceAsStream("main/resources/foo.properties");
Properties properties = new Properties();
properties.load(input);
// ...
As to the #{resource} syntax, this is indeed specifically for CSS/JS/image resources placed in /resources folder of public web content. See also How to reference CSS / JS / image resource in Facelets template?

Related

JBoss equivalence of glassfish alternate docroot

This is very easy using Glassfish:
Consider my absolute path on unix /apps/static_content/.
Using Glassfish, I will simply define alternate doc root as:
<property name="alternatedocroot_1"
value="from=/static/* dir=/apps/static_content/"/>
When I upload my images and other data files, I can save them to the /apps/static_content directory, and within my JSF page I can display my static content normally as:
<p:graphicsimage value="/static/external_web_app.png"/>
I really need to achieve the same functionality in JBoss AS7
How can I do this?
This question is a little bit old, but answering if someone need to do this with newer versions of JBoss/Wildfly.
JBoss AS was renamed to Wildfly from version 8 (i.e. Wildfly 8 is JBoss AS 8) and Red Hat supported version of JBoss was renamed to JBoss EAP. So this applies to:
Wildfly (any version)
JBoss EAP (from version 7)
First thing to note is that "Alternate doc root" feature in glassfish doesn't work like that. Please take a look at this question for an explanation of the behavior of this feature: Alternate docroot not working on glassfish 4
Now, to answer this question, JBoss/Wildfly is build upon Undertow, that let you do exactly what you need. Refer at this question on how to configure undertow as a web server for static content: How to configure Wildfly to serve static content (like images)?
Option 1: You could try to deploy a separate exploded .war file, and use that for your static content
In your case: In .../jboss-7/standalone/deployments/ there must be a static.war/.
So the uploads go into this directory, and the content is served back the normal way.
As for details, see Is it possible to deploy an exploded war file (unzipped war) in JBoss AS 7
As pointed out by BalusC: You must not redeploy/remove this directory, as soon as data has been uploaded. You should have a regular backup of this directory.
As far as I know this is the only possibility to do it by configuration/setup only.
Option 2: Create separate webapp with name static.war. Add a servlet to stream the static content
This way there is no need to upload/store the files into the file system below ../deployments/, it could be any directory, but you need an additional servlet, so it's solved programatically.
A simple streaming servlet could look like this (just streaming - no authentication etc.):
public class DownloadServlet extends HttpServlet {
#Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
final File dir = new File("/var/cms/storage/");
final String start = "/static/";
final String relativePath = request.getRequestURI().substring(
request.getRequestURI().indexOf(start) + start.length());
final File file = new File(dir, relativePath);
final String ct = URLConnection.guessContentTypeFromName(file.getName());
response.setContentType(ct);
final InputStream is =
new BufferedInputStream(new FileInputStream(file));
try {
final boolean closeOs = true;
org.apache.commons.fileupload.util.Streams.copy
(is, response.getOutputStream(), closeOs);
} finally {
is.close();
}
}
Map all URLs to this servlet:
<servlet-mapping>
<servlet-name>DownloadServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
The name static.war provides the /static/ web context, so that should make it compatible with the URLs in your code.
If you explore jboss directory you will find that there are many you can use to store different type of data like jboss.serer.data.dir .
You can try asking this directory path via system properties and store in a folder the data your services are using, under such directory.
String path = System.getProperty("jboss.server.data.dir");
Then you can use path as you want, if is just static as shown in your example you set directly the name of the directory.
This should work, I hope :p
ps: as the previous answer suggest the saved data will keep in the directory, and you must not redeploy/remove this directory.. It will keep your data there..

Java getResourceAsStream cannot load resource

I am trying to load a class whose name is specified in a properties file. Here is the code for the same.
try {
Properties properties = new Properties();
InputStream in = MyAbstractFactory.class.getResourceAsStream("/some.properties");
properties.load(in);
String impl = properties.getProperty("key");
MyAbstractFactory factories = (MyAbstractFactory) Class.forName( impl ).newInstance();
return factories;
} catch (Exception e) {
return new DefaultFactoriesImpl();
}
This code is part of a jar file. the properties file is just outside the jar. Its unable to load the properties file and is loading DefaultFactoriesImpl instead. I know this happens when MyAbstractFactory.class.getResourceAsStream cant find the resource in the class path but that doesn't seem to be the case here.
Dir Structure:-
com
myjar.jar
some.properties
Command i am executing is "java -jar myjar.jar"
Any feedback on why this might be happening. Could this have something to do with Clasloaders? I'd like to add that when i run this code from within eclipse it seems pick up some.properties just fine.
Remove the leading slash from the argument you pass to getResourceAsStream().
Put the folder outside the JAR into the CLASSPATH when you execute the JAR. I don't know if the manifest CLASSPATH overrides the one you might pass using -cp. Play with it; one of them will work.
It's not finding your .properties file because it's not in the JVM CLASSPATH. When you do it properly, the JVM will find it.

How to load resource bundle in a servlet (JSF)

I am using JSF and have made a custom servlet for loading images dynamically.
I want this servlet to pick up my image folder location i.e. "F:\photos\images\" from the resource bundle. How do I access my resource bundle defined in my faces-config?
I do not want to hard code this value in the servlet :/
Let's say you have a resource bundle defined like this:
<resource-bundle>
<base-name>/resources/bundle</base-name>
<var>bundle</var>
</resource-bundle>
In Java you can access this property file like this:
import java.util.ResourceBundle;
...
ResourceBundle rb = ResourceBundle.getBundle("/resources/bundle");
String val = config.getString(key);

Accessing properties file in a JSF application programmatically

I am trying to access the i18n properties file I'm using in my JSF application in code. (The idea is to have a page that displays its keys and values as a table actually.)
The project is a maven project, and in the src/resources/localization folder, and deployed in the war file in WEB-INF\classes\localization\
java.util.Properties prop = new java.util.Properties();
String path = "localization/stat_codes.properties";
InputStream foo = prop.getClass().getResourceAsStream(path);
But the variable foo turns out to be null whatever I set the path variable to, /WEB-INF/classes/localization/stat_codes.properties, "localization.stat_codes.properties" etc. A similar question is here, but there is no helpful answer there as well.
The Class#getResourceAsStream() can take a path which is relative to the location of the Class which you're using there as starting point. So, for example, if the class is located in the com.example package and you request the path foo/filename.properties, then it will actually load the com/example/foo/filename.properties file. But if you use /foo/filename.properties, then it will actually load foo/filename.properties from the classpath root.
So, your code
java.util.Properties prop = new java.util.Properties();
String path = "localization/stat_codes.properties";
InputStream foo = prop.getClass().getResourceAsStream(path);
will actually look for java/util/localization/stat_codes.properties file.
But in applications with a complex multiple classloader hierarchy, the one classloader isn't the other. The classloader which loaded the core Java classes does not necessarily have knowledge about files which are in the webapp's /WEB-INF/classes. So prefixing the path with / will not necessarily be the solution, it would still return null.
If you can guarantee that the current class is visible by the same classloader as the properties files (because they're in the same sub-root of the classpath, e.g. /WEB-INF/classes, then you should indeed use
String path = "/localization/stat_codes.properties";
InputStream foo = this.getClass().getResourceAsStream(path);
But if at some point, the properties files will be externalized because of more easy maintenance/editing during runtime so that you don't need to rebuild/redeploy/restart the webapp whenever you want to edit the files, then the above line of code will likely fail as well. The externalized location would be only accessible by a different classloader. The canonical solution is to use the thread's context classloader as starting point instead, it has access to all resources in the classpath.
String path = "localization/stat_codes.properties";
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream foo = loader.getResourceAsStream(path);
(note that this one cannot take a path starting with /, it's always relative to the common root)
See also:
Where to place and how to read configuration resource files in servlet based application?
ExternalContext#getResourceAsStream() returns null, where to place the resource file?
It seems that the culprit is the prop object, I supposed any object would work, but it has to be the current object (this) on which the method getClass() is invoked, it seems. Also, the path should start with a / since the localization directory resides in WEB-INF/classes.
String path = "localization/stat_codes.properties";
InputStream foo = this.getClass().getResourceAsStream(path);

JSF/Seam messages.properties without _xx which language is that?

i just wanted to know as which language the default messages.properties is read.
i thought that it is the in the faces-config.xml configured default locale is:
<locale-config>
<default-locale>de</default-locale>
<supported-locale>de</supported-locale>
<supported-locale>en</supported-locale>
</locale-config>
it contains no <message-bundle> tag,i created a messages.properties, messages_en.properties and messages_de.properties. To access the values i use this code
ResourceBundle resourceBundle = SeamResourceBundle.getBundle();
String bundleMessage = resourceBundle.getString("key.something");
In the menu i used this to show (and switch) the language what works fine
<h:selectOneMenu value="#{localeSelector.localeString}">
<f:selectItems value="#{localeSelector.supportedLocales}"/>
</h:selectOneMenu>
Now it doesn't matter what language i select, je always uses the messages.properties and not _de or _en. Do i need a concrete class for <message-bundle> to find also the _de and _en resource bundles?
EDIT:
ResourceBundle resourceBundle = SeamResourceBundle.getBundle();
java.util.Locale locale = resourceBundle.getLocale();
Contains always the correct locale de or en but always uses messages.properties and if this file is deleted, returns just the key as if he found no other file. The messages*.properties are in the /WEB-INF/classes folder.
i tried now to take Map<String, String> messages = org.jboss.seam.international.Messages.instance(); It contains also the values from messages.properties and not _de or _en
Using #{messages[key.label]} in the *.xhtml file also returns just the messages.properties values but not from _de or _en.
But a messages_de properties or _en directly in the xyz.war file with a <a4j:loadBundle var="i18n" basename="messages"/> does work. (thats how i did the i18n in the "not Java" frontend)
two more tries always return just the default properties and not _de or _en
resourceBundle = context.getApplication().getResourceBundle(context, "messages");
java.util.Locale locale = new java.util.Locale("de");
resourceBundle = ResourceBundle.getBundle("messages",locale);
if i create a new messages2_de.properties and *_en* and use the code above, everything works fine.
java.util.Locale locale = new java.util.Locale("de");
resourceBundle = ResourceBundle.getBundle("messages2",locale);
EDIT (Apparently JBoss Seam is a bit different)
As this document says, you probably should not instantiate bundles yourself.
Instead, you would define bundles you want to use and let Seam read message for you:
#In("#{messages['Hello']}") private String helloMessage;
Generally getBundle() method of any of ResourceBundle derived implementations will give you invariant bundle if you omit Locale parameter. This is by design.
If you need to access localized version, you need to get Locale from UIViewRoot:
Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
ResourceBundle resourceBundle = SeamResourceBundle.getBundle();
String bundleMessage = resourceBundle.getString("key.something");
I am not aware how your localeSelector bean is coded, but it too should set Locale in UIViewRoot.
Normally, the bundle without _xx is simply the bundle that is used if the key is not found in any of the more specific bundles for the current language.
Although I don't know what SeamResourceBundle exactly does, you do have to tell it somewhere what the 'current' language is. You say switching the language works, but what exactly do you do upon switching? At what point do you execute SeamResourceBundle.getBundle()?
Is key.something actually defined in all 3 bundles?
My bad. You couldn't find the error. The project had one messages.properties in /WEB-INF/classes and a second set(but without default properties) directly in the web content directory with the same names.
So i guess he took the only existing default messages.properties from the classes folder and the messages_de/en.properties from the web-content folder.

Resources