I'm working on a text-based game as a practice project.
I already managed to write an engine that displays information and handles user input. It draws the games content from custom Scene classes. More precisely, I have a Scene superclass and create child-classes, like EvilDungeon extends Scene, for the actual playable levels.
All scenes are located in a "Scenes"-folder, but I want to be able to make game expansions later on by dropping additional .class files of new scenes into said folder.
My plan was to use loadClass when the game is started to add them to a Scene-array, but it requires the class names which I can't know, since there can a any random combination of scenes in the Scenes-folder.
How can I load all scenes in the folder without knowing which scenes exactly are present? Can I retrieve the class name somehow?
My plan was to use loadClass when the game is started to add them to a Scene-array, but it requires the class names which I can't know, since there can a any random combination of scenes in the Scenes-folder.
That's what Service Provider Interfaces are for. Just define an interface for your plugins and use ServiceLoader to get an instance of each plug-in.
The plug-in needs to provide a text-file in /META-INF/services named after the interface and containing the class which implements that interface (both names must be fully qualified). You'll find the details in the documentation of ServiceLoader.
This gives you the ability to add plugins to the class-path. E.g. you can run your application using something like java -cp myapp.jar;plugins/* my.app.Main, then all the plugin-Jars in folder plugin will be added to the class-path and are available to your application (for more information on the asterisk, see this answer).
If you want to load plug-ins at runtime, you can use a URLClassLoader, e. g. read this question or this tutorial on how to use the URLClassLoader.
Related
I've mostly only created application for personal use and the rare occasions where I have distributed my code have been in the form of uploading my source code on GitHub. I'm currently finishing up a project and plan on using launch4j to package it up as an exe. However, my application has a handful of png files that I coded in with the unique filepath of my computer. Obviously if my code were to run on any other computer in the world, those files would not be found.
I'm vaguely aware that java does not require the full filepath for a file (ie C:\Users...\file_name.ext) but I've never gotten a program to run correctly unless I write out the filepath like that, so that's been my default up until this point.
The resource system. Think about it: What's the difference between the many class files that comprise your application, and those png files, from an application distribution perspective?
The answer is, essentially, nothing. They are file-like concepts, they might prefer to be shipped in a packaged-up file (a jar file) instead of separately. They must be found at some point halfway through your app's existence (java does not pre-load all classes. It just loads your main class, and then loads whatever is needed the first time you mention any class).
You don't have to hardcode the absolute path to those class files in your app, so they clearly don't suffer from this 'coding filepaths' issue.
Thus, the answer is somewhat obvious: Simply stick those PNG files in the exact same place as your class files, and ask the VM to provide you with the data in them using whatever mechanism it is using itself, as it is doing that exact same job (find resource, obtain data in the resource) all the time, on your class files.
But, how?
You have 2 different methods, and these 2 methods take the same kind of argument, which comes in 2 forms: A grand total of 4 'modes' to choose from.
Pick a method
If the API you have that needs an image file so happens to have an overload that accepts a URL, this is very simple (ImageIcon is one such resource, that's probably what you're passing these PNG files to, so that's great):
URL loadIcon = ContextClass.class.getResource("/icons/load.png");
new ImageIcon(loadIcon);
Quite simple. Sometimes you want to read it yourself directly, and a URL object is rather unwieldy. Sometimes, you want to pass it to an API which does not have a URL overload, but it does have an InputStream overload. Then, you can fetch an InputStream. Given that this is a resource, like all resources, you must safely close it, thus, let's use try-with:
byte[] pngData;
try (var in = ContextClass.class.getResourceAsStream("/icons/load.png")) {
pngData = in.readAllBytes();
}
ContextClass.class is a somewhat exotic java syntax feature: It is an expression that resolves to the java.lang.Class instance of the so-named class. For example, Class<?> c = String.class; is legal java and gives you the class object that represents the class concept of all java.lang.String objects. The class object itself has these getResource methods. Thus, substitute some relevant class that you wrote as context here. Presumably, if you want to load an image in source file MyStatusWindow.java, you'd just use that class: MyStatusWindow.class.getResource.
These methods will look in the same location that the class itself was loaded from. If ContextClass is loaded from a jar, then the system will fetch PNGs from within that jar. If it's loaded from a build dir during development/debug, the png is loaded from there. If you've got some fancypants module system that is loading classes straight from the network, then the PNG will also be loaded from there.
resourceKey
A resourcekey is simply a path. It's not really a path, just - a string with slashes. You can't use .., for example, it's not really a path. You also, weirdly, can't use filenames that include more than a single dot in the name, for historic (read: silly) reasons.
You have 2 variants - classpackage relative and absolute.
.getResource("/icons/load.png") is absolute. .getResource("icons/load.png") is relative. The leading slash is the difference.
If you have:
package com.foo;
public class MyStatusWindow {
...
MyStatusWindow.class.getResource("icons/load.png");
}
And this is all in a jar file (i.e. /com/foo/MyStatusWindow.class is one of the entries listed if you execute jar tvf myapp.jar on the command line), then the above would look in that jar for /com/foo/icons/load.png - the relative form takes the context-class's package and sticks it in front. The absolute form would just look in /icons/load.png, still in the jar (so it's never C:\ - never the root of your disk - it's the root of the classpath entry).
Build systems
Maven, Gradle, and just about every other build system has a proscribed directory structure. The above example should go in src/main/java/com/foo/MyStatusWindow.java, relative to some 'root project dir'. Only java source files are supposed to go there. There's also a resources: src/main/resources/com/foo/icons/load.png, that's where your icon file would go. Then MyStatusWindow.getResource("icons/load.png") will just work, in your build system, and in your IDE, and when you ship it all as a jar file. If it doesn't, you've misconfigured your IDE or have a broken build configuration - and you should fix that. Out of the box, this just works.
I'm running a Processing sketch (a PApplet subclass, created by exporting from a Processing sketch) from Clojure. The basic process is
export sketch
locate jar files in exported application
install these jar files into local maven repo
load the libs in clojure using lein
The code is in this GH repo, including (reasonably) minimal instructions for how to get set up and reproduce the problem.
Loading basic sketches works, but when I create a sketch which calls e.g. loadImage() to load an image from my data folder, the sketch can't find it.
I know this is convoluted way to do this - this is partially a learning exercise for me and partially the fact that I'm a lisp guy but not a JVM guy.
Is there any way to dynamically tell the PApplet subclass where its data folder is? Or am I stuck putting absolute paths everywhere?
EDIT: Ugh, what a mess. It looks like the problem is that I'm actually wrapping each PApplet subclass in a clojure proxy (so that I can intercept certain methods and make the various sketches play nice together) but the way Processing finds the data path involves looking at the ProtectionDomain to find the root source path, so this won't work as I'm currentyly trying it. Back to the drawing board...
So you're using maven to link to jars you've exported from Processing, and those jars can't find the sketch directory?
If I were you, I would look int two places:
The Processing JavaDoc contains a detailed reference on every variable and function in Processing. Particularly check out the PApplet class and check out the following:
ARGS_SKETCH_FOLDER
selectInput()
selectOutput()
sketchFile()
sketchOutputPath() and sketchOutputStream()
sketchPath()
I would bet that by either setting the first variable or by overriding some of those functions, you can have Processing look wherever you want.
If you need more detail, the Processing source will tell you exactly how Processing uses all of the above.
This might sound like a ridiculous question, but I have to ask it because I have a working product which is doing this.
I have an applet running inside a browser. This applet is just not just any applet, but a fairly complex package application for CRM/ERP. I was told by a vendor company that they are able to monitor what a user does inside the applet, by replacing applet's main class at runtime before launch with their own. The term used was "endorsing".
I am a bit clueless now. How can you look inside an applet and listen on user clicks and keyboard events, even if you could somehow hack into it? I can tell you that this is a true story, because I have seen this vendor company's applicaiton and it just sits in the background and records all the contextual information (for instance, user filled which textbox in the applet, the name of the textbox and etc).
Are they any hacks at classloading level (I feel stupid asking this), or something else that I have not come across in java that would let you do something 'urban legendary' like this?
Java Applets are loaded using a HTML tags like this:
<applet archive="ApplicationSP1.jar,Application.jar" code="Main.class" name="myApp" width="800" height="600"></applet>
As you can see, the "archive" attribute supports several .jar files.
You could use this technique to load your own versions of the Java Classes of the application by putting them in the ApplicationSP1.jar file. They will be loaded before those classes stored in the second Application.jar.
Obviously, you would need to do some reverse engineering to understand which classes from the original application to override or wrap. Then you have to create new ones named exactly (same package and class name) as those you want to override.
Other option would be developing Aspects to capture events in the application and load these aspects using same technique of multiple .jar in the archive attribute of the HTML applet tag.
The solution for capturing Swing/AWT event can be found in
Want javax.swing hook that tells me WHICH component in the hierarchy is executing an action
It is difficult for overwriting Swing/AWT class used by applet which launching from browser.
They have to breaking the protection of Java security manager and get writing permission of JRE endorsed library folder.
For this case, Java Endorsed Standards Override Mechanism is hard to implement without manually operation of end user.
No tutorial presents a concrete example of how an internationalization plug-in fragment is created an used. I need translations to the plugin.xml's and source code files. Trying to wrap my head around where the translations go, and where the i18n facade goes.
1. How does that fragment apply to an multi-plugin enterprise application, and more importantly, how do all those plugins externalize their strings inside appropriate folders in the fragment?
2. What about external JARs? How does the mechanism provide translation support for external resources?
3. With the risk of being a long-shot, would it be possible to provide independent translation of a view or perspective? Not necessarily at runtime, because I know bundles can't be dynamically switched.
There is some help available, this article lists the process. It's based on Eclipse 2.0 (!) but the basic ideas are still correct.
An even better article is this tutorial by Vogella
For each plugin you need to translate, you will create one plugin fragment. The fragment is associated to one host plugin, so you need several fragments. Each fragment can contain several languages though. The languages are separated by the folder structure as described in Step 5 in the first article
I am guessing you are referring to non-eclipse Java jars that you have made yourself, yes? If so, that is a completly different process, best suited for a separate question with the Java tag. Oracle has a guide that may help. But keep in mind that you only need to translate the content that the user is exposed to. So a refactor to keep all user visible strings to the Eclipse plugins might be a good idea.
Are you referring to the name of the view/perspective? If so, yes. You can translate the information you give in your plugin.xml aswell. See Vogellas article, chapter 3
Edit:
At nr.3 I was referring to choosing which View to translate (e.g. via a view menu), then restart the app, then only the said view should translate
Well.. I think of a way that would work in theory, but I'm not sure its the best alternative.
So translatation is based on locale. And given the locale a certain translation is chosen. If no appropriate translation exists, a default will be selected.
So if your View menu were to change the locale of the application to, say "us-en-v1", and you only had one view which had a translation for the locale "us-en-v1", that would mean that particular view would be translated, but the rest of the application would use a default (could be that they default back to the closest translation, dont remember exactly).
Then for each view you would create a new translation and use a unique locale for each.
That should work, but it abuses the way translations are supposed to work, so it could lead to issues.
I've done something similar at one time, one app was used in the same language but different customers had different vocabulary. So we used the i18n to make the application use the right terms by defining our own locales.
We are using http://babel.eclipse.org/babel/ to let people translate existing ressources. The build process adds the required language fragments to the artefact. Each plug-in defines its own Messages.properties / Messages.java file.
I guess, you can't do much about external jars.
For instance:
public final class MyMessages {
// a string member as you reference it later in the code
public static String login_window_user_label;
// static initializer which initalizes the fields in this class
static {
NLS.initializeMessages("mymessages", MyMessages.class);
}
}
And (usually in the same package) you have a properties file, in this case,
mymessages.properties
which includes the string:
login_window_user_label = Enter username to login to {0}
And within the code you do:
String userNameLabel = NLS.bind(MyMessages.login_window_user_label, Environment.getName());
Thats how we make our bundles "translatable". The build generates the language fragments and the babel server instance allows the translation.
This is also a design question. I don't know the best way to design having many activities and how to break them up into packages while still accessing the apps resources.
I have an application which allows a user to login and access a tabbed activity which gives access to many modules. If they click a module they will load the module which will in turn contains a tabbed activity.
Currently i have 3 packages within this app:
com.appname.app (many activities i'd like to split up, login, module list etc)
com.appname.app.XML (xml handlers)
com.appname.app.Utils (static util classes etc)
I'd like to add a new package to contain all my modules so something like
com.appname.app.Modules ( or even a seperate package for EACH module)
But i can then no longer access the main apps resources without referring to them with a full package name such as setContentView(com.appname.app.R.layout.channel_list);
Obviously within any com.appname.app activities i just reference the resources such as layouts via setContentView(R.layout.channel_list);.
I'd have thought that a sub package (sub directory) should have access to a parent's resources without having to use com.appname.app.R.layout.channel_list instead of R.layout.channel_list. Which is why i'm assuming something is wrong with my design
The same issue goes for accessing string, drawable etc resources...
Im sure this is something very basic I'm missing. I've tried manually importing the "parent" package and that didn't work either.
is this how i should be doing things? or should i somehow be creating the resource files within each module package? or there another way i should be handling the design/split up of my app?
Since the Android pre-compiler would compile all references to a R.class file and place in in the main package there's no better way than to explicitly import the resources wherever you want to use them
import com.appname.app.R;
so that no full qualifier is needed.