In Java VM, a classloader can have multiple domains, generally JAR files (which are similar to APK files). Each of these domains have a single namespace. That is, every resource has a unique name.
Calling ClassLoader.getResources("foo") can, therefore, return multiple URLs. However, looking at Android, the build seems to flatten the resource namespace, overwriting resources with the same path. This wreaks havoc with mechanisms like the Java ServiceLoader. It also seems to void any use of the META-INF/MANIFEST.MF.
For example, if I use liba.jar and libb.jar, and both have a resource OSGI-OPT/com.example.Foo, then only one seems to end up in the APK.
Are the rules around these class resources documented somewhere?
Are the rules around these class resources documented somewhere?
The Android build behavior when creating APKs is described in the Add app resources section of the user guide. Look for the heading on Resource merging.
You are correct that all of the sources are combined into a single namespace. The idea is that resource merging makes the resulting APK smaller1.
Android is not constrained to be Java(tm) compliant, and they have made different design decisions.
1 - An APK is just like a fat JAR or uberJAR in the classic Java world. You would not expect multiple copies of a resource in a fat JAR. The getResources API would only give you a single resource for any name in that context as well.
Related
This question already has answers here:
Is the use of Java's default package a bad practice?
(3 answers)
Closed 6 years ago.
I'm pretty new to Java, and I know what packages do. You use them to sort multiple files of a Java application together. However, is it standard to put it in a package if your application only has a single class? What are the pros and cons of doing this?
EDIT: I'm packaging this single class into a .jar file after.
From oracle documentation, it is clear that
The primary motivation for jar development was so that Java applets
and their requisite components (.class files, images and sounds) can
be downloaded to a browser in a single HTTP transaction, rather than
opening a new connection for each piece. This greatly improves the
speed with which an applet can be loaded onto a web page and begin
functioning. The JAR format also supports compression, which reduces
the size of the file and improves download time still further.
Additionally, individual entries in a JAR file may be digitally signed
by the applet author to authenticate their origin.
From Package Documentation of Oracle,
For small programs and casual development, a package can be unnamed
(ยง7.4.2) or have a simple name, but if code is to be widely
distributed, unique package names should be chosen using qualified
names. This can prevent the conflicts that would otherwise occur if
two development groups happened to pick the same package name and
these packages were later to be used in a single program.
It really depends on how you're compiling and running the program, but ultimately it's your choice.
Let's have a look at some of the different ways you might build your program.
1. Compiling the file with javac
If you're compiling the file using javac then the package will not matter. It will generate the .class file the same directory as the source file.
2. Compiling to a JAR File
If you're compiling to a JAR File, then the .class file will be inside the directories specified in your package name. Although this would not affect how the program is ran.
In both of these cases, I'd say that the package identifier is unnecessary for a single-file program. However, there is an exception.
The exception...
If ever you plan to use the class in a larger program, then adding a relevant package name would be essential.
This is because it would...
Prevent name collisions when other classes in the default packages have the same name.
Help people know whether or not your class is the one they want.
Can you imagine if 20 different developers made a List class in the default package, and somehow they all ended up in a project? It would be chaos! How would I choose the right List?
So in the case of writing a class that others will use in their own projects, you should definitely use package names.
It is probably non-production application if it has a single class and doesn't have any dependencies or resource files. So it is completely up to you how you will start your app.
If you want to distribute your app - make it in compliance with the standards, put it in a jar, publish to maven...
Java classloaders identify your classes by concatenating the package and the class name. So, if you don't use packages, the probabilities of name collisions are higher. Even though your app consists of only one class, you're going to reference many others, explicitly or not.
Also consider that you'll only be able to build very trivial applications with only one class, so probably that condition won't last forever.
Besides that, a class without package is a border case, so you'll probably find many tools that don't work with it.(I had this problem with the Web Service builder tool for Eclipse).
In short, use a package. It won't cause you any trouble, and (at least potentially) will save you many.
I am trying to read a static file from within App Engine module that is placed inside WEB-INF directory.
...
File file = new File("WEB-INF/somestaticfile.ext);
...
It perfectly works in cloud environment, however, in development server it ends up with FileNotFoundException.
I build and run the app with the aid of maven and it seems that it is also needed to prepend a directory representing the module inside the project's EAR hierarchy so that the file would be found.
...
File file = new File("modulename.war/WEB-INF/somestaticfile.ext);
...
This works in local development server but not in cloud environment.
I believe that there is a way how to read a file that would work in both cloud and local environment. Any advice would be appreciated.
I have spent some time to figure it out and here are solutions I have found. If you have your resources placed on classpath then you can access them as follows from both cloud and local dev server.
getClass().getClassLoader().getResource("resourceName");
or
getClass().getClassLoader().getResourceAsStream("resourceName");
However, if your resources are place somewhere else (e.g. WEB-INF), the only way I have found is by means of javax.servlet.ServletContext#getResource(String) or javax.servlet.ServletContext#getResourceAsStream(String) that also work in both environments. This solution is perfectly OK if you access resources from web layer of your app but if you need to access them from other layers (e.g. service layer), it is not probably a good idea to introduce a dependency to Java Servlet API to that layer.
Since I am using Spring, I decided to load resources via org.springframework.core.io.ResourceLoader#getResource(String) that returns a resource abstraction whose concrete implementation depends on the application context type and a resource path prefix. For applications running in a web container, the actual implementation uses ServletContext methods under the hood but you are shielded from it.
If you do not use Spring, it should not be a big deal to implement your own resource abstraction based on ServletContext.
If anyone could provide other solutions, feel free to do so.
An excellent resource, as always, are the Google developer documents:
https://developers.google.com/appengine/docs/java/config/appconfig
That said, to help others in future, I will summarize some relevant points that I think generally relate to the question (since I can't see what is happening your specific environment).
App Engine serves static files from dedicated servers and caches that are separate from the application servers. Files that are accessible by the application code using the file system are called resource files. These files are stored on the application servers with the app.
By default, all files in the WAR are treated as both static files and resource files, except for JSP files, which are compiled into servlet classes and mapped to URL paths, and files in the WEB-INF/ directory, which are never served as static files and always available to the app as resource files. You can adjust which files are considered static files and which are considered resource files using elements in the appengine-web.xml file. The element specifies patterns that match file paths to include and exclude from the list of static files, overriding or amending the default behavior. Similarly, the element specifies which files are considered resource files.
You mentioned that there seemed to be a disconnect between the development server and deployment. Without diving into Maven, my first thought was to look at path patterns. As they note on the Google Developers documentation, path patterns are specified using zero or more and elements. In a pattern, * represents zero or more of any character in a file or directory name, and ** represents zero or more directories in a path. Files and directories matching patterns will not be uploaded when you deploy your app to App Engine. However, these files and directories will still be accessible to your application when running on the local Development Server. An element overrides the default behavior of including all files. An element applies after all patterns (as well as the default if no explicit is provided).
Hope that helps provide a general perspective. If you find a specific solution to the issue and can share it here, please do!
I'm aware that it isn't easily feasible to get all of the classes in a package using reflection, but I'm wondering if someone knows of a good solution/workaround, specifically for an Android project?
Given a package, I need to be able to retrieve all of the classes from it and process annotations from them using reflection.
Does anyone know of a way to do this? Are there any libraries available?
Scanning the filesystem as most solutions for non-Android Java do won't help on Android. Here's a (theoretical) solution that is android-specific: http://mindtherobot.com/blog/737/android-hacks-scan-android-classpath/
However, it remains a hack, since Java unfortunately does not directly support this.
Existing dependency injection solutions use reflection for processing the annotations, but still need the resources to be declared. See this example of DI using reflection.
If you are using Ant to build your artifacts, you could read the contents of your source directory using Bash or Java, and use this to regenerate the full hierarchy of classes automatically during each build. This might make things tricky if you rely on heavily on the Eclipse IDE though, since the list might be out of date until you run another Ant build. (Note: according to Pyscho you can make Eclipse use Ant by altering the project configuration, see comments)
Another option might be to process the AndroidManifest file using the AssetManager, but you would be limited to the resources declared in that file. The compiled classes themselves are in-lined and optimised in the classes.dex file, and as such you're unlikely to get much useful information from it.
I think you might find the answer here https://stackoverflow.com/a/1457971/1199538
there is a java file attached so you can download it and try it
short snippet from the answer following:
This method can only be used when:
You have a class that is in the same package you want to discover, This class is called a
SeedClass. For example, if you want to list all classes in 'java.io', the seed class may be java.io.File.
Your classes are in a directory or in a JAR file it has source file information (not source code file, but just source file). As far as I've tried, it work almost 100% except the JVM class (those classes come with the JVM).
Your program must have permission to access ProtectionDomain of those classes. If your program is loaded locally, there should be no problem.
You can do classpath scanning for Android at compiletime, before the JVM bytecodes have been converted to Dalvik bytecodes, e.g. using the ClassGraph library (I am the author):
https://github.com/classgraph/classgraph/wiki/Build-Time-Scanning
I'm adding some interception routines to Dalvik libcore methods (e.g. file open method in libcore/luni/src/main/java/org/apache/harmony/luni/platform/OSFileSystem.java), which I think only changes basic sharing libraries. But to my surprise, every time I run make after modifications, it rebuilds nearly everything of the framework, such as Calculator application, W3C DOM parser, etc. It really takes time to build the framework after a small modification. I'm wondering if it is possible to reduce number of rebuilt components after modifying dalvik libcore? Thanks.
It actually isn't too surprising that changing core.jar causes many things to be rebuilt. core.jar contains many/all of the core java classes, like Object, String etc. So that every other jar/apk that gets built actually depends on core.jar.
From a makefile perspective, it has no clue what you changed in core.jar, and whether it is safe to not rebuild all these other things that depend on core.jar. It simply sees that the last modified time on core.jar is newer than on all of the other jars/apk that depend on it, so it rebuilds them all.
The trick, however, is to tell make specifically what you want to build, instead of telling it to build everything.
Assuming that you have already done a full build previously, you can simply do
make core snod
The core target will specifically build a new core.jar with your changes, without rebuilding anything that depends on core.jar.
And the snod target (short for systemimage-nodeps) will cause it to repackage everything from out/target/product//system into a new system.img. This is a "special" target that is declared in build/core/Makefile.
In general, the target for a particular jar/apk is simply the name of that jar/apk, without the extension. Alternatively, you can look at the Android.mk file for that module, and find the module name, which is typically something like LOCAL_PACKAGE_NAME or LOCAL_MODULE, depending on the type of module.
For core.jar (in gingerbread at least), the module name is in libcore/JavaLibrary.mk (which is actually included by libcore/Android.mk). This file contains definitions for a number of different modules, but the first one, with LOCAL_MODULE := core is the one resposible for building core.jar. The rest seem to mostly be test related modules.
I have two jar files from a client, one of which is used for a testing and another for final versions. Currently I put them in different folders and modify the library path when deploying our code, but it would be nice to be able to load both jar files and switch between them dynamically at runtime.
Is this possible?
You can always write your own ClassLoader and chain it with the standard ClassLoader.
http://download.oracle.com/javase/6/docs/api/java/lang/ClassLoader.html
I used this method 10 years ago to load classes that were recieved via sockets and specified in an XML file (via sockets as well). My java program didn't know that the classes even existed before it got the XML file and the classes.
Using OSGi bundles you can do that. Take a look at http://blog.springsource.com/2008/02/18/creating-osgi-bundles/. Search for "multiple versions".
justinjh,
chrisparker2000's suggestion looks to be the most feasible - You have to write a custom classloader, the only change that I can think of is something along the following lines:
1. For the client deliverable jars - say client.dev.jar and client.prod.jar, rename to a different extension and place these in the classpath. Rename to a different extension to prevent the container from loading the contents of the jar.
Using the custom classloader, load the contents on demand, based on the solution offered by chrisparker2000, by placing a small facade on top of the client classes, say ClientClassFactory which based on the environment(dev/prod/anything else) would use the custom classloader to load from either client.dev.otherext or client.prod.otherext .
If you use a build-tool like maven, you can define different jar files (dependencies) for different scopes (test vs production).
You may also use maven profiles to define different set of jar files/versions.