Please note: Although this question specifically calls out Swing, I believe this to be a pure Guice (4.0) question at heart and which highlights a potential generic issue with Guice and other opinionated frameworks.
In Swing, you have your application UI, which extends JFrame:
// Pseudo-code
class MyApp extends JFrame {
// ...
}
Your app (JFrame) needs a menubar:
// Pseudo-code
JMenuBar menuBar = new JMenuBar()
JMenu fileMenu = new JMenu('File')
JMenu manageMenu = new JMenu('Manage')
JMenuItem widgetsSubmenu = new JMenuItem('Widgets')
manageMenu.add(widgetsSubmenu)
menuBar.add(fileMenu)
menuBar.add(manageMenu)
return menuBar
And your menu items need action listeners, many of which will updated your app's contentPane:
widgetsSubmenu.addActionListener(new ActionListener() {
#Override
void actionPerformed(ActionEvent actionEvent) {
// Remove all the elements from the main contentPane
yourApp.contentPane.removeAll()
// Now add a new panel to the contentPane
yourApp.contentPane.add(someNewPanel)
}
})
And so there is intrinsically a circular dependency:
Your app/JFrame needs a JMenuBar instance
Your JMenuBar needs 0+ JMenus and JMenuItems
Your JMenuItems (well, the ones that will update your JFrame's contentPane') need action listeners
And then, in order to update the JFrame's contentPane, these action listeners need to reference your JFrame
Take the following module:
// Pseudo-code
class MyModule extends AbstractModule {
#Override
void configure() {
// ...
}
#Provides
MyApp providesMyApp(JMenuBar menuBar) {
// Remember MyApp extends JFrame...
return new MyApp(menuBar, ...)
}
#Provides
JMenuBar providesMenuBar(#Named('widgetsListener') ActionListener widgetsMenuActionListener) {
JMenuBar menuBar = new JMenuBar()
JMenu fileMenu = new JMenu('File')
JMenu manageMenu = new JMenu('Manage')
JMenuItem widgetsSubmenu = new JMenuItem('Widgets')
widgetsSubmenu.addActionListener(widgetsMenuActionListener)
manageMenu.add(widgetsSubmenu)
menuBar.add(fileMenu)
menuBar.add(manageMenu)
return menuBar
}
#Provides
#Named('widgetsListener')
ActionListener providesWidgetsActionListener(Myapp myApp, #Named('widgetsPanel') JPanel widgetsPanel) {
new ActionListener() {
#Override
void actionPerformed(ActionEvent actionEvent) {
// Here is the circular dependency. MyApp needs an instance of this listener to
// to be instantiated, but this listener depends on a MyApp instance in order to
// properly update the main content pane...
myApp.contentPane.removeAll()
myApp.contentPane.add(widgetsPanel)
}
}
}
}
This will produce circular dependency errors at runtime, such as:
Exception in thread "AWT-EventDispatcher" com.google.inject.ProvisionException: Unable to provision, see the following errors:
1) Tried proxying com.me.myapp.MyApp to support a circular dependency, but it is not an interface.
while locating com.me.myapp.MyApp
So I ask: what's the way of circumventing this? Does Guice have an API or extension library for dealing with this sort of problem? Is there a way to refactor the code to break the circular dependency? Some other solution?
Update
Please see my guice-swing-example project on GitHub for a SSCCE.
There are several techniques available to refactor a circular dependency so that it is no longer circular, and Guice docs recommend doing that whenever possible. For when that is not possible, the error message Guice provided hints at the Guice way to resolve this in general: separate your API from your implementation by using an interface.
Strictly speaking it is only necessary to do this for one class in the circle, but it may improve modularity and ease of writing test code to do it for most or every class. Create an interface, MyAppInterface, declare in it every method that your action listeners need to call directly (getContentPane() seems to be the only one in the code you posted), and have MyApp implement it. Then bind MyAppInterface to MyApp in your module configuration (in your case simply changing the return type of providesMyApp should do it), declare the action listener provider to take a MyAppInterface, and run it. You might also need to switch some other places in your code from MyApp to MyAppInterface, depends on the details.
Doing this will allow Guice itself to break the circular dependency for you. When it sees the circular dependency, it will generate a new zero-dependency implementing class of MyAppInterface that acts as a proxy wrapper, pass an instance of that into the action listener provider, fill out the dependency chain from there until it can make a real MyApp object, and then it will stick the MyApp object inside Guice's generated object which will forward all method calls to it.
Though I used MyAppInterface above, a more common naming pattern would be to actually use the MyApp name for the interface and rename the existing MyApp class to MyAppImpl.
Note that Guice's method of automatic circular dependency breaking requires that you not call any methods on the generated proxy object until after initialization is complete, because it won't have a wrapped object to forward them to until then.
Edit: I don't have Groovy or Gradle ready to try to run your SSCCE for testing, but I think you are very close to breaking the circle already. Annotate the DefaultFizzClient class and each of your #Provides methods with #Singleton, and remove the menuBar parameter from provideExampleApp, and I think that should make it work.
#Singleton: A class or provider method should be marked #Singleton when only a single instance of it should be made. This is important when it is injected in multiple different places or requested multiple times. With #Singleton, Guice makes one instance, saves a reference to it, and uses that one instance every time. Without, it makes a new separate instance for each reference. Conceptually, it's a matter of whether you are defining "how to make an X" or "this here is the X". It seems to me that the UI elements you're creating fall in the latter category - the singular menu bar for your app, not any arbitrary menu bar, etc.
Removing menuBar: You've already commented out the one and only line in that method that uses it. Simply delete the parameter from the method declaration. As for how to get the menu bar into the app anyway, that is already handled by the addMenuToFrame method combined with the requestInjection(this) call.
Perhaps it might help to do a run through of the logic Guice will go through if these alterations are made:
When you create the injector with an ExampleAppModule, it calls your module's configure() method. This sets up some bindings and tells Guice that, whenever it's done with all the bindings setup, it should scan the ExampleAppModule instance for fields and methods annotated with #Inject and fill them in.
configure() returns and, with bindings setup complete, Guice honors the requestInjection(this) call. It scans and finds that addMenuToFrame is annotated with #Inject. Inspecting that method, Guice finds that an ExampleApp instance and a JMenuBar instance are needed in order to call it.
Guice looks for a way to make an ExampleApp instance and finds the provideExampleApp method. Guice examines that method and finds that a FizzClient instance is needed to call it.
Guice looks for a way to make a FizzClient instance and finds the class binding to DefaultFizzClient. DefaultFizzClient has a default no-args constructor, so Guice just calls that to get an instance.
Having acquired a FizzClient instance, Guice is now able to call provideExampleApp. It does so, thereby acquiring an ExampleApp instance.
Guice still needs a JMenuBar in order to call addMenuToFrame, so it looks for a way to make one and finds the providesMenuBar method. Guice examines that method and notes that it needs an ActionListener named "widgetsMenuActionListener".
Guice looks for a way to create an ActionListener named "widgetsMenuActionListener" and finds the providesWidgetsMenuActionListener method. Guice examines that method and finds that it needs an ExampleApp instance and a JPanel named "widgetsPanel". It already has an ExampleApp instance, acquired in step 5, and it got that from something marked as #Singleton, so it reuses the same instance rather than calling provideExampleApp again.
Guice looks for a way to make a JPanel named "widgetsPanel" and finds the providesWidgetPanel method. This method has no parameters, so Guice just calls it and acquires the JPanel it needs.
Having acquired a JPanel with the right name in addition to the already-made ExampleApp, Guice calls providesWidgetsMenuActionListener, thereby acquiring a named ActionListener instance.
Having acquired an ActionListener with the right name, Guice calls providesMenuBar, thereby acquiring a JMenuBar instance.
Having finally, at long last, acquired both an ExampleApp instance and a JMenuBar instance, Guice calls addMenuToFrame, which adds the menu to the frame.
Now after all that has happened, your getInstance(ExampleApp) call in main gets executed. Guice checks, finds that it already has an ExampleApp instance from a source marked #Singleton, and returns that instance.
Having looked through all of that, it seems #Singleton is strictly necessary only on provideExampleApp, but I think makes sense for everything else too.
Well, if you do have a circular dependency, it can't be a creational circular dependency - since otherwise you wouldn't be able to create that cycle of objects at all.
You can perform post-creation configuration by requesting injection on a module. For example, you can use this to add the JMenuBar to the JFrame:
class MyModule extends AbstractModule {
#Override void configure() {
requestInjection(this);
}
#Inject void addMenuToFrame(JFrame frame, JMenuBar menu) {
frame.setMenuBar(menu);
}
}
I've not tested this to see if it works (as you've not given enough code to try it on) - so you might need to experiment with where the right place is to break the current cycle in order to subsequently join it like this - but something like this should work for you.
I've answered a few other questions about injecting into guice modules like this - maybe have a look at those too, since there are some great tips from others about this too, rather than me trying and failing to repeat them correctly here.
I've been playing with this a bit more to help my understanding, and I've found that you need your JFrame and JMenuBar to be bound in singleton scope for this to work usefully, because the method injection occurs upon creation of the injector, not upon subsequent creation of JFrame and JMenuBar instances (probably obviously).
I'm answering this without being able to test the answer against your code, because I don't know (and I don't want to learn) Groovy.
But basically, when you have circular dependencies, the best way to break it is to use a Provider.
package so36042838;
import com.google.common.base.*;
import com.google.inject.Guice;
import javax.inject.*;
public class Question {
#Singleton public final static class A {
B b;
#Inject A(B b) { this.b = b; }
void use() { System.out.println("Going through"); }
}
#Singleton public final static class B {
A a;
#Inject B(A a) { this.a = a; }
void makeUseOfA() { a.use(); }
}
public static void main(String[] args) {
Guice.createInjector().getInstance(B.class).makeUseOfA(); // Produces a circular dependency error.
}
}
This can then be rewritten like this:
package so36042838;
import com.google.common.base.*;
import com.google.inject.Guice;
import javax.inject.*;
public class Question {
#Singleton public final static class A {
B b;
#Inject A(B b) { this.b = b; }
void use() { System.out.println("Going through"); }
}
#Singleton public final static class B {
Supplier<A> a;
#Inject B(Provider<A> a) { this.a = Suppliers.memoize(a::get); } // prints "Going through" as expected
void makeUseOfA() { a.get().use(); }
}
public static void main(String[] args) {
Guice.createInjector().getInstance(B.class).makeUseOfA();
}
}
Related
I have a question about how to conceptually create an Observer and link it to another class: I currently have a class called Simulation that is supposed to create TransactionCreated objects and publish them as events. Another class called TransactionReceiver is supposed to be an Observer of every event that is published by the Simulation class and work with them.
The main method is included in the Simulation class and starts by creating an event in a static context and publishing it which works. My question would be how I am supposed to connect the TransactionReceiver as an Observer and let it subscribe to those events by receiving them in a method and work with those received objects? Do I need to create another class that would include the main method and create a Simulation and TransactionReceiver object that are then linked together as Observable and Observer? How would that look like?
And if I would extend that system with several different classes would they all have to be linked together through one class that connects Observers and Observables?
Your app should only have one main method.
Conceptually, this should be where you do the initial setup of Simulation and TransactionReceiver, so perhaps you could move it to a separate class to help you visualise how things should work. You could try something like below:
class Application {
private Simulation simulation;
private TransactionReceiver transactionReceiver;
public Application() {
simulation = new Simulation(/* params here*/);
transactionReceiver = new TransactionReceiver(/*params here*/);
}
public void go() {
simulation.simulate().subscribe(transactionCreated -> transactionReceiver.doSomething(transactionCreated);
}
public static final main(String[] args) {
Application application = new Application();
application.go();
}
}
Eventually as you get more fluent you could think about adding a dependency-injection framework like Guice or Dagger.
This will help you with managing the dependencies of the classes that you need to use throughout your application.
So you would end up with a more simple Application - it would just set up the DI-framework and then you can use the classes how you want to.
UPDATE:
If you want to communicate between two different classes, you will need to use methods:
class Simulation {
public Observable<TransactionCreated> simulate() {
// use PublishSubject or whatever
}
}
I have a huge Part source code I have to touch at 1 place. It is violating a lot of principles so I would like to extract at least the function I had to modify which is a #UIEventTopic handler. There are no tests and I would like to add them here so I know I do not break existing functionality.
I would like to move away from this:
public class MyPart {
...
#Inject
#Optional
public void event(#UIEventTopic(EVENT) EventParam p) {
...
}
}
To something like this:
public class MyPart {
...
}
public class MyEventHandler {
#Inject
#Optional
public void event(#UIEventTopic(EVENT) EventParam p, MyPart part) {
...
}
}
With the Eclipse DI I see no easy way of creating an instance of the handler class. It cannot be a #Singleton because it is a Part which can have multiple instances, and adding the handler to the IEclipseContext in the #PostConstruct is ugly because it adds a circular dependency between the part and the handler. Is there a magic how I can enforce the instantiation through the e4xmi files, or some alternative way?
My current solution is to extract purely the functionality to a utility bean and return the data and set it on the part, but this is also something not too nice (requires a lot of additional null-checks, ifs, etc.).
I am not entirely sure that I understand your question, however, this is how I would proceed:
Extract Delegate
Move the code in event() to the MyEventHandler so that MyClass fully delegates the event handling
public class MyPart {
#Inject
#Optional
public void event( #UIEventTopic(EVENT) EventParam param ) {
new MyEventHandler().handleEvent( this, param );
}
}
class MyEventHandler {
void handleEvent(MyPart part, EventParam param) {
// all code from event() goes here
}
}
This should be a safe-enough refactoring to do without having tests - and in the end, you don't have a choice as there are no tests.
Ensure the Status Quo
Now I would write tests for handleEvent(), mocking the required methods of MyPart and thus make sure that I won't break existing behavior.
Implement new Feature
After that I would be able to make the desired changes to MyEventHandler::handleEvent in a test driven manner.
Clean Up
Then I would extract an interface from MyPart that has only those methods required for MyEventHandler to do its work. If said interface gets too big, it would indicate that there is more refactoring left to do.
I have this code that initializes Calligraphy default configuration.
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
// The initialization I want to move
CalligraphyConfig.initDefault(new CalligraphyConfig.Builder()
.setDefaultFontPath("fonts/MyFont.ttf")
.build()
);
}
}
I want to use Dagger 2 in my project but I don't fully understand what classes should I create and where to move this code in order to keep the project clean ?
In short, you probably wouldn't move anything. The problem with this library is that it uses static methods for initialization and utilization. Static methods are a pain when trying to do dependency injection.
The Library (or why you would not change anything)
It looks like this library is 'just' about switching the used fonts by wrapping the context. As such it does not really provide business logic to your project, but just adds to your views / UI.
Injecting a dependency rather than just calling static methods is most useful if you either want to be able to unit test (inject mocks) or easily swap modules / behavior. In the case of globally changing fonts, both seems less likely.
If on the other hand you really need (or want to) be able to test it, or just have a clean design...
...wrap it
Static methods are a pain, because you can not have objects holding the logic. Unless you wrap them. To properly do DI with static methods, you would have to define your own interface.
public interface CalligraphyManager {
/**
* Called on app start up to initialize
*/
void init();
// other methods, like wrapping context for activity
Context wrap(Context context);
}
You now have some manager to access the static methods. The implementation should be fairly simple, since you want to do proper DI the application context and path needed for init() would be passed into the constructor of your implementation. The creation of your manager can thus be handled by your ApplicationModule—just add some provides method
#Singleton
#Provides
// You would also have to provide the path from somewhere or hardcode it
// left as an exercise for the reader
CalligraphyManager provideCalligraphyManager(Context context, String path) {
return new ActualCalligraphyManager(context, path);
}
Your application would then look something like this:
public class MyApplication extends Application {
#Inject
CalligraphyManager mCalligraphy;
#Override
public void onCreate() {
super.onCreate();
mComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this))
.build();
mComponent.inject(this);
// call the initialization
mCalligraphy.init();
}
}
Everything else is as usual. You have a singleton object in your application components graph, you can thus inject the same object into your activities and call `wrap´ where appropriate.
What about testing / mocking?
Since the whole reason of doing this is to make it 'testable', you can now easily provide a mock / stub object.
Create another implementation of the manager where init() would just do nothing, and wrap(Context) would just return the same context—a simple stub object.
I am somewhat new to the OSGi world. And some concepts still elude me.
I'm trying to create a graphical OSGi application using Swing, Equinox and Declarative Services. The goal is to ease the creation of plugins and extensions for the application.
I have stumbled with a design problem and, since I am doing this from the ground up, I want to use all the best practices I can.
I do have a bundle that contains the API and only exposes interfaces to be implemented as services.
public class SomeClass {
}
public interface Manager<T> {
void add(T obj);
void update(T obj);
void remove(T obj);
}
public interface SomeClassManager extends Manager<SomeClass> {
}
public interface Listener<T> {
void added(T obj);
void updated(T obj);
void removed(T obj);
}
public interface SomeClassListener extends Listener<SomeClass> {
}
Let's say I have a bundle (Core) that provides a service that is a manager of certain types of objects (It basically contains an internal List and adds, removes and updates it).
public class SomeClassCoreManager implements SomeClassManager {
private ArrayList<SomeClass> list = new ArrayList<SomeClass>();
private ArrayList<SomeListener> listeners = new ArrayList<SomeListener>();
protected void bindListener(SomeListener listener) {
listeners.add(listener);
}
protected void undindListener(SomeListener listener) {
listeners.remove(listener);
}
public void add(SomeClass obj) {
// Adds the object to the list
// Fires all the listeners with "added(obj)"
}
public void update(SomeClass obj) {
// Updates the object in the list.
// Fires all the listeners with "updated(obj)"
}
public void remove(SomeClass obj) {
// Removes the object from the list.
// Fires all the listeners with "removed(obj)"
}
}
I also have a second bundle (UI) that takes care of the main UI. It should not "care" for the object managing itself, but should be notified when an object is added, removed or changed in order to update a JTree. For that purpose I used a Whiteboard pattern: The UI bundle implements a service that is used by the Core bundle to fire object change events.
public class MainWindow extends JFrame {
private JTree tree = new JTree();
private SomeClassManager manager;
protected void activate() {
// Adds the tree and sets its model and creates the rest of the UI.
}
protected void bindManager(SomeClassManager manager) {
this.manager = manager;
}
protected unbindManager(SomeClassManager manager) {
this.manager = null;
}
}
public class SomeClassUIListener implements SomeClassListener {
public void added(SomeClass obj) {
// Should add the object to the JTree.
}
public void updated(SomeClass obj) {
// Should update the existing object in the JTree.
}
public void removed(SomeClass obj) {
// Should remove the existing object from the JTree.
}
}
My problem here is the following:
The MainWindow is a DS component. I am using its activator to initiate the whole UI. The instance creation is handled by OSGi.
In order to get the updates from the manager, I am exposing the SomeClassUIListener as a Declarative Service. Its instance is also handled by OSGi.
How should I access the instance of the JTree model from the SomeClassUIListener?
I have come up with several options but I am not sure which to use:
Option 1:
Use some kind of internal DI system for the UI bundle (like Guice or Pico) and put it in a class with a static method to get it and use it all over the bundle.
This approach seems to be frowned upon by some.
Option 2:
Inject a reference to the MainWindow (by turning it into a service) in the SomeClassUIListener through OSGi and go from there. Is this possible or advisable? Seems to me that it is the simpler solution. But, on the other hand, wouldn't this clutter the bundle with component config files as the UI got more and more complex?
Option 3:
Create a separate bundle only for listeners and use OSGi to update the MainWindow. This seems to me a bit extreme, as I would have to create an enormous amount of bundles as the UI complexity grows.
Option 4:
Use the MainWindow class to implement the Listener. But, the more services in the main UI bundle, the bigger the MainWindow class would be. I think this would not be a good option.
I cannot think of more options. Is any of the above the way to go? Or is there another option?
Thank you in advance.
Edit:
Just to clarify as Peter Kriens had some doubts about this question.
My goal here is to decouple the user interface from the Manager. By Manager I mean a kind of repository in which I store a certain type of objects (For instance, if you consider the Oracle's JTree tutorial at http://docs.oracle.com/javase/tutorial/uiswing/components/tree.html, the manager would contain instances of Books).
The Manager may be used by any other bundle but, according to my current plan, it would notify any listener registered in it. The listener may be the main UI bundle but may also be any other bundle that chooses to listen for updates.
I am not sure I completely grasp your proposal, and it feels like you are on your way to create a whole load of infrastructure. In OSGi this is generally not necessary, so why not start small and simple.
Your basic model is a manager and an extension. This is the domain model and I would try to flow things around here:
#Component(immediate)
public class ManagerImpl { // No API == immediate
List<Extension> extensions = new CopyOnWriteArrayList<Extension>();
JFrame frame = new JFrame();
#Reference(cardinality=MULTIPLE)
void addExtension( Extension e ) {
addComponent(frame, e.getName(), e.getComponent());
extensions.add(e);
}
void removeExtension( Extension e) {
if ( extensions.remove(e) ) {
removeComponent(frame, e.getName());
}
}
#Component
public class MyFirstExtension implements Extension {
public String getName() { return "My First Extension";}
public Component getComponent() { return new MyFirstExtensionComponent(this); }
}
Isn't this what you're looking for? Be very careful not to create all kinds of listeners, in general you find the events already in the OSGi registry.
Some option here would be to pass the tree model instance as the argument in the listeners methods.
public void added(JTree tree, SomeClass obj)
This way listeners manager would be responsible only for listeners logic, not for the tree state.
Another nice option would be to create a separated TreeProviderService, responsible for holding and serving singleton JTree instance for the application. In such case you would consume TreeProviderService directly from the listeners.
I propose to simply also use DS for the UI creation and wiring. If you use the annotations Peter mentioned you will not clutter your bundles with component descriptors in XML form.
So your listener is a #Component and you inject the UI elements it needs to update into it.
Btw. what you plan to do sounds a bit like databinding to me so you should also investigate what these offer already.
See: Swing data binding frameworks
Btw. you may also want to look for more advanced frameworks than swing. For example some time ago I did a small tutorial for vaadin: https://github.com/cschneider/Karaf-Tutorial/tree/master/vaadin
It already has a databinding for java beans. So this made it really easy for me to code the UI. The full UI is just this little class: https://github.com/cschneider/Karaf-Tutorial/blob/master/vaadin/tasklist-ui-vaadin/src/main/java/net/lr/tutorial/karaf/vaadin/ExampleApplication.java
In the old version I still needed a bridge to run vaadin in OSGi but version 7 should be quite OSGi ready.
From the perspective of a cross application/applet java accessibility service, how would you link to a package but only optionally execute an action based on existence/availability of a package (being already loaded) at runtime?
I think what I'm interested in here is a way to resolve the class identity crisis but rather than the issue being between 2 apps sharing objects, being a service loaded at a higher level of the class loaders.
It seems like reflection is the way to go, but I am not sure how or if I can implement a derived class this way. I need to add a specific listener derived from the specific optional classes, I can load the listener using the applet class loader but the internals still fail. Say you wanted to add an JInternalFrameListener, but Swing wasn't guaranteed to be available, using reflection you can find the method to add the listener, but how can you create and have the frame listener work if it cannot find any of the related classes because they can't be found in the base classloader! Do I need to create a thread and use setContextClassLoader to the classloader that knows about swing so that I can get the class to be loaded reliably? simply trying to set the class loader on my existing thread didn't seem to work.
Earlier description of issues
Sorry, I'm not quite sure what to ask or how to make this clear, so it rambles on a bit.
Say a class uses some feature of another, but the other class may not always be available - say finding the website from JNLP if this is a JNLP app.
At one stage I thought that simply compiling against JNLP would mean that my class would not load unless JNLP was available, and so to identify this optional section I simply wrapped a try{} catch( NoClassDefFoundError ) around it.
Later something changed (perhaps changing jdk or ?? I don't recall) and it seemed that I should also use a try{} catch( ClassNotFoundException ).
Now I wanted to extend this idea to other optional features, but it doesn't seem to work consistently.
Say I wanted to add some feature to do something more advanced in a JRE1.6 runtime using the same jar and classes as I run in a JRE1.3, or say I want to handle some controls in a specific gui toolkit which may not always be used like SWT or oracle.forms.
Is there some way of doing this more reliably? It just seems wrong to cause an exception and catch it to ignore it all the time.
The current issue comes down to being able to compile against oracle.forms but then the accessibility component installed in ext is unable to access the oracle.forms classes even though objects from the oracle.forms package have been created. If I throw the frmall.jar into the ext directory to test then the accessibility component works up to the point that the whole lot gets flakey because of the different versions of the same package.
I seem to be caught up on an issue with the class loader not being the right one or something (??). How do I find the right one?
Edit:
The answers so far are kindof interesting but not quite getting me where I want to be.
In the case of the gui components I currently compile in the form of a factory something like...
import oracle.forms.ui.*;
import java.awt.*;
static public IComponentNode newNode( INode parent, Component component ) {
System.out.println( component.getClass().toString() );
try{
if( component instanceof FormDesktopContainer )
... does stuff here like return new FormDesktopNode( parent, (FormDesktopContainer) component )
} catch ( NoClassDefFoundError a ) {
System.out.println( a.getMessage() );
}
where it prints out class oracle.forms.ui.FormDesktopContainer and then throws and exception on the instanceof call with NoClassDefFound thus printing out oracle/forms/ui/FormDesktopContainer
So how can it have an instance of a class yet not be able to find it?
How about this? messy, but it ought to work:
public boolean exists(String className){
try {
Class.forName(className);
return true;
}
catch (ClassNotFoundException){
return false;
}
}
You can check the availability of a class by calling
ClassLoader.getSystemClassLoader().loadClass("my.package.MyClass")
if it throws a ClassNotFoundException, it's not available. If you get the Class object, it is. You can then choose behaviour based on whether or not the class is available.
I suggest compiling the majority of your code against your minimum target. Have code that uses particular optional libraries clearly separated, but dependent upon the bulk of your code. Dynamically load the code that uses optional libraries once. The main class should do something that checks for the presence of the required library/version in its static initialiser.
In the case of JNLP, your JNLP main class load the JNLP dependent code statically.
(Note that attempting to catch class loading related exceptions from normally linked code is unreliable.)
getSystemClass loader was not useful for this purpose as there where multiple possible class loaders to interact with based on which applet the given window was in. The accessibility components being loaded at a more base class loader cannot see the applet specific classes.
To interact with the objects reflection does the job, though it does add so much more to maintain.
// statically linking would be
return component.getText();
// dynamically is
try {
return (String)component.getClass().getMethod("getText", new Class [] {}).invoke(component, new Object [] {});
} catch (Throwable e) {
e.printStackTrace();
}
The trickier bit is in writing a class derived from an interface that is not directly accessible, using the Proxy service allows this to be accomplished, providing the proxy service the applet specific class loader and the dynamically loaded class for the interface.
public void addListener(Container parent) {
if (parent == null) { return; }
if ("oracle.forms".equals(parent.getClass().getName())) {
// Using the class loader of the provided object in the applet
// get the "class" of the interface you want to implement
Class desktopListenerClass = Class.forName( "oracle.DesktopListener"
, true, parent.getClass().getClassLoader());
// Ask the proxy to create an instance of the class,
// providing your implementation through the InvocationHandler::invoke
Object desktopListener = Proxy.newProxyInstance(
parent.getClass().getClassLoader()
, new Class[] { desktopListenerClass }, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if ("functionName".equals(method.getName())) {
// do stuff
}
return null;
}
});
// do something with your new object
Method addDesktopListener = parent.getClass().getMethod("");
addDesktopListener.invoke(parent, desktopListener);
}
}
examples cut down to show general method