Java resource bundle is loaded from another module - java

Currently my project contains 3 modules, 1 named generic (which is based for other 2) ,
first named Romania and second named Cyprus, inside of them i have a little code (which does not contain the logic of calling bundles). In the all 3 modules i have resources -> language and here i have first_en_US_CUSTOM (Cyprus module) , first_en_US_CUSTOM (generic module) and same in the Romania. The logic to load this is:
private String getTranslated(ResourceBundle resourceBundle) { // resource bundle comes with locale en_US
String result = null;
try {
String text = resourceBundle.getString(key);
result = text;
} catch (MissingResourceException var7) {
System.out.println("No resource found");
}
return result;
}
While calling this (it is called from a JAR file) , it returns the string from Cyprus module, however Romanian module is running (obv. it should take it from Romanian module). if i remove the first_en_US from Cyprus module then i get it in Romanian moudule and generic one, however -> it takes from generic module now. What is the problem here? Where should i search? I am debugging and can't find at all the problem or where it loads from.

First (if you should not know it already): since java 9 *.properties can be edited in UTF-8, without \uXXXX which for Greek is great. The problem here is class path/locale related.
The Locale adds a suffix to the file. It could also be a problem using the same path to the properties but different contents. A problem when falling back on the generic root locale. (Maybe java's module system might help.)
ResourceBundle resourceBundle = ResourceBundle.getBundle("foo.bar.Baz", locale);

Related

Eclipse RCP: InternalPlatform.getDefault().getUserLocation() doesen't return C:\Users\username\AppData\Roaming

I am currently migrating an Eclipse 3.0 application to 4.4. The user data was and still should be stored in the folder C:\Users\username\AppData\Roaming\applicationname
The application is using following code to read the directory:
public static String getUserDirectory()
{
String directory = InternalPlatform.getDefault().getUserLocation().getFile();
return directory;
}
I know the code is deprecated, but following code returns the same:
public static String getUserDirectory()
{
String directory = Platform.getUserLocation().getURL().getFile();
return directory;
}
They both return C:\Users\username\user but as I said the user data should be stored at C:\Users\username\AppData\Roaming\applicationname. Did the behaviour of those methods change?
How can I realize that I store my user data under C:\Users\username\AppData\Roaming\applicationname and my application can still find the directory?
I know this has to do something with environment-variables which I don't fully understand.
I don't have a 3.x target platform at hand to compare but C:\Users\username\user looks plain wrong.
If you are interested in the details, the constructor of EquinoxLocations computes the userLocation and adds the literal 'user' the the user's home directory if no default is specified.
Hence, if you start your application with -user #user.home or -Dosgi.user.area=#user.home, the user location will be set to C:\Users\username\. Still not what you are looking for, but at least a sane value.
I think this is a bug in Equinox and recommend to file a bugzilla. If it turns out that there is a good reason for this appraoch the bug entry will still serve as documentation/reasoning.
In the meanwhile you could obtain the home directory on Windows through System.getenv( "APPDATA" ). According to this post it will return the roaming home directory.
I solved the problem by adding three properties in the Configuration tab of my config.ini.product-file:
osgi.configuration.area =
#user.home/AppData/Roaming/ApplicationName/...
osgi.user.area =
#user.home/AppData/Roaming/ApplicationName/...
osgi.instance.area =
#user.home/AppData/Roaming/ApplicationName
Now my method as stated in my question reads the paths that are configured by those properties and the config.ini file which is generated looks exactly like the one of the old build with Eclipse 3.0.

Using corn-cps to scan for config files matching wildcard pattern

I'm trying to scan my classpath for config files matching a certain pattern. I'm using corn-cps.
The file I'm looking for is packaged in a jar and I can find it using java's default
MyClass.class.getClassLoader().getResource("jmulticonfig.3.properties")
returns
jar:file:/tmp/testjar.jar!/jmulticonfig.3.properties
I would like to find all jmulticonfig.*.properties so my corn-cps code is
List<URL> resources = CPScanner.scanResources(
new ResourceFilter()
.packageName("*")
.resourceName("jmulticonfig.*.properties")
);
An empty List is returned when I run this.
Anyone with corn-cps experience can help or suggest some other way?
Edit: To take the good suggestion of #approxiblue, the code can be found at https://github.com/kanesee/jmulticonfig.
Please make sure to add src/main/resources/jmulticonfig-3.jar to your classpath. It contains jmulticonfig.3.properties, the file which I'm trying to read using corn-cps
I think you've found some kind of bug in the corn-cps library because if you debug their classes running your code, it seems that there is a point where the resource name gets lost.
Anyway the following JmultiConfig.getConfig() implementation finds all the resources you're searching with a regular expression:
public static synchronized Properties getConfig() {
System.out.println("---");
if (s_props == null) {
List<URL> resources = CPScanner.scanResources(new ResourceFilter().archiveName("*"));
for (URL resource : resources) {
if (resource.getFile().matches(".*jmulticonfig\\..*\\.properties$")) {
System.out.println("*** -> " + resource);
}
}
}
return s_props;
}
The regular expression can be modified to match anything you need.
You are trying to obtain resources from the default package "".
But corn-cps has a bug reading resources from that package (the condition in line 63 of net.sf.corn.cps.RootedUrl fails for these resources).
For a workaround you could place your resources in another package, e.g. package res and filter for that package:
List<URL> resources = CPScanner.scanResources(
new ResourceFilter().packageName("res").resourceName("jmulticonfig.*.properties")
);
Also a filter .packageName(*) finds resources in all packages, except the default package.
The solution a Max also works, but comes at the price to construct a URL object for every resource in the classpath (except rt.jar which is not covered by corn-cps).

getResourceAsStream from JUnit

I am creating a JUnit TestCase for a project which needs to load a configuration file during initialization.
This configuration file is inside the project in src/main/resources/config folder and during the build maven places it into /config folder, inside the JAR.
The initialization class, reads the file from there using this statement:
ClassLoader classloader = this.getClass().getClassLoader();
BufferedReader xmlSource = new BufferedReader(new InputStreamReader(classLoader.getResourceAsStream("/config/config.xml")));
The problem I have is that when I deploy and execute this jar into the application server it works as expected, however, whenever I run it in a JUnit TestCase within Eclipse, the getResrouceAsStream method returns null.
Considering that the class is my.package.MyClassTest.java, and that it lives in src/test/java/my/package/MyClassTest.java, I already tried placing a copy of the config.xml file into the following folders without success:
- src/test/resources/config
- src/test/resources/my/package/config
- src/test/java/my/package/config
I know that similar questions have been asked many times here in StackOverflow, but all the responses I found refer to changing the way the file is loaded and, although changing the code may be an option, I would prefer to just find the right place for the file so I do not need to modify things which already work in the production environment.
So, where should I place this file to be able to use it in my JUnit test?
UPDATE
I just came up with the solution with a small change in the code:
Instead of using the ClassLoader to get the resource, I directly used the class:
Class clazz = this.getClass();
BufferedReader xmlSource = new BufferedReader(new InputStreamReader(clazz.getResourceAsStream("/config/config.xml")));
And it reads the file successfully from src/test/resources/config/config.xml.
However, there's is something very weird here:
The Class.getResourceAsStream method is:
public InputStream getResourceAsStream(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResourceAsStream(name);
}
return cl.getResourceAsStream(name);
}
And if I debug it, I can clearly see that this getClassLoader0() returns exactly the same object (same id) than the previous call, this.getClass().getResourceAsStream() (which I maintained, just to compare the values)!!!
What's going on here?!
Why does calling the method directly not work, while inserting a new method call in between works?
Honestly, I'm really astonished in front of this.
BTW, I am using JUnit version 4.10. May it be tampering the getClassLoader call in some way?
Many thanks,
Carles
Replying as to your question
And if I debug it, I can clearly see that this getClassLoader0() returns exactly the same object (same id) than the previous call, this.getClass().getResourceAsStream() (which I maintained, just to compare the values)!!!
What's going on here?!
Why does calling the method directly not work, while inserting a new method call in between works?
The difference between calling
this.getClass().getClassLoader().getResourceAsStream("/config/config.xml");
and calling
this.getClass().getResourceAsStream("/config/config.xml");
Lies in the exact source that you were showing from Class:
public InputStream getResourceAsStream(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResourceAsStream(name);
}
return cl.getResourceAsStream(name);
}
But the problem is not with what getClassLoader0() returns. It returns the same thing in both cases. The difference is actually in resolveName(name). This is a private method in the Class class.
private String resolveName(String name) {
if (name == null) {
return name;
}
if (!name.startsWith("/")) {
Class<?> c = this;
while (c.isArray()) {
c = c.getComponentType();
}
String baseName = c.getName();
int index = baseName.lastIndexOf('.');
if (index != -1) {
name = baseName.substring(0, index).replace('.', '/')
+"/"+name;
}
} else {
name = name.substring(1);
}
return name;
}
So you see, before actually calling the classLoader's getResourceAsStream(), it actually removes the starting slash from the path.
In general, it will try to get the resource relative to this when it doesn't have a slash, and pass it on to the classLoader if it does have a slash at the beginning.
The classLoader's getResourceAsStream() method is actually intended to be used for relative paths (otherwise you would just use a FileInputStream).
So when you used this.getClass().getClassLoader().getResourceAsStream("/config/config.xml");, you were actually passing it the path with a slash in the beginning, which failed. When you used this.getClass().getResourceAsStream("/config/config.xml"); it was kind enough to remove it for you.
getResourceAsStream function from ClassLoader object will not remove slash prepended with your search string since the search will be done relative to the classpath. i.e searches the resource where search path used to load classes.
Say for example if your class yourpackage/Test.class, which is located under /a/b/c/d/yourpackage/Test.class, loaded by system classloader(i.e default classloader) and your classpath must point to /a/b/c/d in order to load the class. Search will be done on to this path.
getResourceAsStream function from Class Object does removes the slash prepended with your search string since the search will be done relative to the class where it resides. i.e searches the resource where your class being loaded from.
Say for example if yourpackage/Test.class loaded from /a/b/c/d/yourpackage/Test.class then the resource path will be /a/b/c/d/yourpackage/config/config.xml
You can test this using following code snipet since both getResource and getResourceAsStream used to follow the same algorithm for search.
System.out.println(Test.class.getClassLoader().getResource("config/config.xml"));
System.out.println(Test.class.getResource("config/config.xml"));
I'm not sure about this, but you could try putting your resources folder in the src/main tree instead of the src/test tree. In some configurations, at least, eclipse copies 'resource' files from src to classes, but not from test to classes. It's worth a shot...

Why I got no super classes with getAllSuperclasses() in JDT API?

I have class A, and class B that inherits A in Eclipse workspace.
The issue that I have is that I got nothing when I tried to get the super types of type B using eclipse JDT API. This is the code (I got the code from - List all subclasses with fully qualified names):
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
java.io.File workspaceDirectory = root.getLocation().toFile();
// 1. The name of the project in the workspace
IProgressMonitor pm = new NullProgressMonitor();
IProject orig = root.getProject(this.projectName);
orig.open(pm);
this.javaProject = JavaCore.create(orig);
orig.refreshLocal(IResource.DEPTH_INFINITE, pm);
// 2. Find the type
IType type = this.javaProject.findType("p.B"); <-- returns correct type info
ITypeHierarchy hier = type.newSupertypeHierarchy(new NullProgressMonitor());
IType[] types = hier.getAllSuperclasses(type);
System.out.println(types); <-- Returns []
I also added the code to refresh/update the resources in package.
IPackageFragmentRoot[] packageFragmentRoots = this.javaProject.getPackageFragmentRoots();
for (IPackageFragmentRoot proot: packageFragmentRoots)
{
proot.getResource().refreshLocal(IResource.DEPTH_INFINITE, null);
}
Everything works fine except getting the hierarchical type information.
What might be wrong? Did I miss any setup before executing the API?
Mine is a headless RCP application.
This may be a temporary solution, but it worked for me.
Short Answer
Make a lib directory, and copy this rtstubs.jar into the directory.
You may need to refresh(F5) the eclipse IDE to see the jar file is included in the project.
Then, in "Java Build Path", you need to add this jar file.
After the inclusion of the jar file in package fragment, you'll get the class hierarchy.
Long Answer (why does this solve the issue)
CompilationUnitDeclaration (org.eclipse.jdt.internal.compiler.ast) and Hierarchy Resolver (org.eclipse.jdt.internal.core.hierarchy)
It has a field ignoreFurtherInvestigation, and a method hasErrors() returns this field.
org.eclipse.jdt.internal.core.hierarchy.HierarchyResolver#resolve() method invokes hasError() to add type information to cache. However, without the inclusion of the jar file, the hasError() method always returns false to prevent any class hierarchical information is stored.
org.eclipse.jdt.internal.core.JavaProjectElementInfo
This class has cache initialization methods such as initializePackageNames and getProjectCache. In getProjectCache() method, package fragment element roots are loaded and added to the cache.
With the rtstubs.jar in the package fragment, the cache now contains all the Java class hierarchy. Without this setup, in the course of cache build up, the ignoreFurtherInvestigation filed is on, and hasError() method returns true not to contain the class hierarchical information to return just nothing.
ADDED
The other solution can be using IRegion.
How can I set the region (=set of java Elements) parameter in JDT TypeHierarchy?

Java, Get all classes available to a URLClassLoader that implement a specific interface

I am working on a command line app that loads user specified text translators at runtime (path to class files/jar provided via command line arg). Basically I am taking that argument and using it to create a URLClassLoader. Then I need to find all classes available to the URLClassloader that implement the Transable interface.
Right now I am only allowing this command line arg to be a directory with class files in it. Making the solution fairly simple (code below). But honestly I don't like the solution as it breaks down for jar files, directory of jar files, etc... Also, this obviously breaks down for any classes with a defined package, as loadClass needs the full name including the package. Anyone have a better method?
File d = new File(path);
if(d.isDirectory()) {
URL url = d.toURI().toURL();
ClassLoader cl = new URLClassLoader(new URL[]{url});
FilenameFilter filter = new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
return name.endsWith(".class");
}
};
for(File f : d.listFiles(filter)) {
String name = f.getName().substring(0, f.getName().indexOf("."));
String key = "";
if(name.endsWith("Translator")) {
key = name.substring(0, name.indexOf("Translator"));
}
else if(name.endsWith("translator")) {
key = name.substring(0, name.indexOf("translator"));
}
else
key = name;
Class c = cl.loadClass(name);
if(Transable.class.isAssignableFrom(c)) {
Transable t = (Transable)c.newInstance();
env.registerTranslator(key, t);
}
else {
System.out.println("[ClassLoader] "+c.getCanonicalName()+" will not be loaded. It is not a translator class");
}
}
}
else {
throw new Error("NOT IMPLEMENTED");
}
You may just have to brute force it if you continue down this road. To my knowledge the default class loader will not even load a class into the JVM unless it is referenced somehow. Which means, some of your classes will be basically invisible unless you know their fully qualified class name to load them.
You may want to reconsider your requirements. As it would be far easier to load a set of Translators that you have been given the class names for.
Instead of searching for implementors explicitly, you should uss Java's Service Provider Interface (SPI) and the ServiceLoader class (introduced in Java 6). SPI is a pretty much a standard way to do what you are describing in Java.
Please see the official Java tutorial on how to create a service provider and how to use it, at runtime, with ServiceLoader.
This may fit the bill. If not, you ought to be able to look through their source to get an idea of what will work for you. http://code.google.com/p/reflections/
In principle this can't work for arbitrary classloaders, as they may use any way imaginable to actually load the classes, and not have any "directory listening" function at all.
A classloader might even generate classes (i.e. the bytecode for the classes) on the fly whenever a loadClass comes, and imagine a TransableClassloader where each such automatically defined class would implement your interface - your program would never end.
That said, for an URLClassloader you can use getURLs(), and for the jar: and file: URL you can use the JarFile or File api to get the list of filenames (and thus Classnames) to try. As you have the root of your package hierarchy given in the URL, finding the right package name is not difficult, too.
(If you need more details, say it.)
Edit: For the package names, they correspond to the directory names inside of your hierarchy. So, when you have a base URL which corresponds to (say) dir/classes, and find a class-file named dir/classes/com/company/gui/SimpleTranslator.class, it corresponds to class com.company.gui.SimpleTranslator.
So, remove the base prefix and replace / by . (and cut of the .class). (In a JarFile you don't have to cut a prefix off.)
Actually, if you use a recursive method to traverse your File hierarchy, you can build up your package-name with the same method, simply by appending Strings (give them as a parameter to the next recursive Invocation):
public void searchClassesInDir(File dir, String packagePrefix) {
if(dir.isDirectory()) {
String prefix = packagePrefix + dir.getName() + ".";
for(File f : dir.listFiles()) {
searchClasses(f, prefix);
}
}
else {
String fileName = dir.getName();
if(! fileName.endsWith(".class"))
return;
String className = packagePrefix + fileName.substring(0, fileName.length()-".class".length());
// now do the rest of your processing
}
}
searchClasses(new File(url.toURI()), "");
Would this help?
Since Class c = cl.loadClass(name);
Class method getInterfaces() returns an array of classes
Check each class name for match to translator class name.

Categories