tag:blogger.com,1999:blog-6266231010760265271.post682112651270563469..comments2024-03-28T05:23:13.144-04:00Comments on Bekwam Blog: Dynamic FX Application Modules - Part 6 - Module DevelopmentCarlhttp://www.blogger.com/profile/15013889141640529637noreply@blogger.comBlogger18125tag:blogger.com,1999:blog-6266231010760265271.post-48167113078318795852016-02-11T10:45:54.081-05:002016-02-11T10:45:54.081-05:00I think OSGI is really solid technology. It's...I think OSGI is really solid technology. It's much better in terms of preventing collisions than what I've laid out here. I was using it for some Talend ESB stuff. However, OSGI still feels a little heavy to me especially if you're not running in an OSGI container.<br /><br />This series also was intended to solve some problems not met by OSGI. Specifically, being able to add parts to a bootstrapped JavaFX application. You can still use the @SubApp annotation even if everything is packed in the same JAR. My particular use case was to chop a project up into JARs and sort it out in JavaWS or in an installer. I'm trying to solve the problem where a huge program (>3k files) starts to buckle when tons of development is added. I'd like to find and focus on any problem pieces through an addition/removal of the suspect component.<br /><br />So, I'm pinning my hopes on classpath issues being solved in Java 9 with modularization. These posts and what I do on my projects might be a stopgap that will work better with Java 9 but won't necessarily be incompatible. As you can see in the "Part 7" post, we're really talking about a minimum of extra code (3 lines) and if all the developers are the same team, then collisions can be kept to a minimum (continuous integration, etc). Then the extra protection of OSGI isn't as compelling given the learning curve and build and deployment differences.Carlhttps://www.blogger.com/profile/15013889141640529637noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-42046323814030441152016-02-11T10:27:42.821-05:002016-02-11T10:27:42.821-05:00Will check the post, thanks!
From what I can see,...Will check the post, thanks!<br /><br />From what I can see, in FXMLLoader 7 uses same Thread.currentThread().getContextClassLoader();<br />that's why couldn't figure out the difference.<br /><br />In general, do you think this approach can be a full alternative to OSGi, i.e. for plugin development? Or in the end it gets too tricky with the all the classloading manipulations?Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-20551563739060178392016-02-11T09:44:36.637-05:002016-02-11T09:44:36.637-05:00Hi,
I'll test this on Java 7 (JavaFX 2.2) whe...Hi,<br /><br />I'll test this on Java 7 (JavaFX 2.2) when I get a chance. I suspect FX more than Guice since you should be running against the same Guice 4.0 in both cases.<br /><br />What I learned from the FX code is that FXMLLoader is using the setContextClassLoader() on the Thread to find classes. I'm not sure what JavaFX 2.2 is using. I heard that the source is only partially open sourced so I'm not sure if I will be able to find an FXMLLoader.java that can give me insight into this. It seems like a lot of code -- not just Guice or FX -- uses Class.loadClass() which can mess up the ClassLoaders.<br /><br />Be sure to check out the latest post on this topic<br /><br />http://bekwam.blogspot.com/2016/02/dynamic-application-modules-part-7.htmlCarlhttps://www.blogger.com/profile/15013889141640529637noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-77677868424049343672016-02-11T09:24:44.454-05:002016-02-11T09:24:44.454-05:00Hey Carl,
Thanks for the approach and great job!
...Hey Carl,<br /><br />Thanks for the approach and great job!<br /><br />I'm trying to make your latest sources work on Java 7,<br />Subapp_A works well, but when I try Subapp_B, then Guice actually throws the following - <br /><br />Guice configuration errors:<br /><br />1) No implementation for com.bekwam.examples.javafx.dynamic.subapp_b.ServiceObject2 was bound.<br /><br />Any ideas if it's any classloaders conflicts or the issue in the Guice bindings (on Java 8 everything is working fine)?<br /><br />ThanksAnonymousnoreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-58171660758215158782016-02-06T09:50:23.805-05:002016-02-06T09:50:23.805-05:00I have this working in the current repos. The UI ...I have this working in the current repos. The UI needs a little work (the import function's uninstall doesn't do anything yet, etc). However, the basics are there. To test this,<br /><br />1. Run the app<br />2. Verify that the main canvas is empty (no JavaFX Buttons)<br />3. Go to File > Preferences<br />4. For each of the subapp jars (_a, _b, _c) in your Maven repository, browse to it<br />5. Select the item and press Import<br />6. Verify in your $HOME/.examples-javafx-dynamic/subapps folder that the 3 items are there<br />7. Restart the app<br />8. Verify that the main canvas has JavaFX Buttons (A, B, C)<br />9. Click on each of the JavaFX Buttons and verify that there are no NPEs<br /><br />I'll be adding a new post on this, but the difference between the working program and your program is probably the following<br /><br />* Add URLs and ClassLoader to Reflections call<br /><br /> Reflections reflections = new Reflections(DEFAULT_PACKAGE_TO_SCAN, cl, urls);<br /><br />* Thread.currentThread.setContextClassLoader() - This is the class loader used by FXMLLoader to find the controller classes (not the controller objects)Carlhttps://www.blogger.com/profile/15013889141640529637noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-61155846063406476032016-02-05T11:15:51.472-05:002016-02-05T11:15:51.472-05:00The current state of the Git repos is a work-in-pr...The current state of the Git repos is a work-in-progress. Everything compiles and runs, but I don't quite have the class loading right yet. - Feb 5Carlhttps://www.blogger.com/profile/15013889141640529637noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-91976260008295370502016-02-03T21:00:10.006-05:002016-02-03T21:00:10.006-05:00Hi,
I'm still working on this. I broke the p...Hi,<br /><br />I'm still working on this. I broke the project I had into GitHub into several Maven modules to simulate the different plugins.<br /><br />I'm working on 3 things.<br /><br />1. Download plugin JARs to a local folder. When the app starts up, it will look into this folder to build the classpath.<br />2. Load the plugins after they are downloaded without a restart. I think this is where you're stuck.<br />3. For completed or dependent plugins, add a restart of the java.exe process to reconsult downloaded JARs (see Step 1).<br /><br />If you pull this, you'll see the new projects. There's a lot of extra stuff.<br /><br />https://github.com/bekwam/examples-javafx-repos1/tree/master/examples-javafx-parent<br /><br />examples-javafx-dynamic is the main app and core module<br /><br />examples-javafx-dynamic-subapp_a, -subapp_b, -subapp_c - JARs that will be loaded into the main app if available<br /><br />examples-javafx-dynamic-framework - annotations and common services shared between plugins and the main app<br /><br />examples-javafx-parent - groups modules and factors dependencies<br />Carlhttps://www.blogger.com/profile/15013889141640529637noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-19555508599907499672016-02-02T11:13:39.017-05:002016-02-02T11:13:39.017-05:00I'll try to post something that integrates the...I'll try to post something that integrates the two.<br /><br />Double-check the URLs sent to your URLClassLoader to make sure that they're finding the appropriate packages. Use an anonymous inner class to extend and create a printPackages() method and output getPackages(). Make sure you see the package containing your class.Carlhttps://www.blogger.com/profile/15013889141640529637noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-62612131451364144622016-02-02T10:58:35.913-05:002016-02-02T10:58:35.913-05:00thanks carl,
actually our method for URLClassLoade...thanks carl,<br />actually our method for URLClassLoader works, and the mainApp can find all the plugin.jar.<br />but if the fxml is loaded correctly through sa.getResource( "fxmlFileName" ),<br />when it tries to load the controller set in the fxml, the MainApp starts looking in its packages, but the plugin controller is in the plugin jar, so it gives my ClassNotFound.<br />Is there a way to get the correct position of the controller class like getResorces() ?<br />I think the GuiceControllerFactory should find it, but it is not. <br />Also following your other tutorial http://bekwam.blogspot.it/2015/06/configuring-google-guice.html and adding bind(BuilderFactory.class).to(JavaFXBuilderFactory.class); in the module, it doesn't work.<br />maybe I'm forgetting to set something in the ControllerFactoryAnonymoushttps://www.blogger.com/profile/06678051192859617766noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-26277026295237330132016-02-02T10:22:48.306-05:002016-02-02T10:22:48.306-05:00Hi,
I think this technique can still help you. I...Hi,<br /><br />I think this technique can still help you. It sounds like you need a little more functionality in establishing the classpath by loading a new plugin JAR. Take a look at this code which uses URLClassLoader to load a JAR file. Once this ClassLoader is setup, I think you can work with the annotation processing to discover the plugin implementations.<br /><br />https://github.com/bekwam/examples-javafx-repos1/tree/master/examples-javafx-parent/examples-javafx-plugin-parent<br /><br />If the plugins become tangled with the running app or other plugins, you might consider restarting the app with an updated classpath. While the app is running, download the JAR to something like ~/.yourapp/plugins. Capture the JVM args and restart the app, killing the previous instance's PID as it starts.Carlhttps://www.blogger.com/profile/15013889141640529637noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-48562967529780902712016-02-02T06:00:42.595-05:002016-02-02T06:00:42.595-05:00My problem is that I'm trying to add new plugi...My problem is that I'm trying to add new plugin.jar after the core is released.<br />so I can't add this jar to the library<br />here a part of my code<br />https://gist.github.com/Filoz/74d02ccd4570ed127f65<br /><br /> Anonymoushttps://www.blogger.com/profile/06678051192859617766noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-69635458469415282822016-02-02T04:00:05.588-05:002016-02-02T04:00:05.588-05:00Hi Carl,
I tested your code and found the same pro...Hi Carl,<br />I tested your code and found the same problem, it's not in the code but in the library.<br />is it correct to add the new plugin not only in the directory "plugins" but also as a library ?<br />with your code, if I add it only in the "plugins" dir, Reflections discovers it and add the new button but if you press it, the error is <br />java.lang.ClassNotFoundException: com.bekwam.examples.javafx.dynamic.subapp_a.ScreenAController<br /><br />if the plugin is also in the library everything works fine. Anonymoushttps://www.blogger.com/profile/06678051192859617766noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-90180011542290226252016-02-01T15:48:21.272-05:002016-02-01T15:48:21.272-05:00Hi,
I found the code and put it into Maven. It...Hi,<br /><br />I found the code and put it into Maven. It's on GitHub.<br /><br />https://github.com/bekwam/examples-javafx-repos1/tree/master/examples-javafx-parent/examples-javafx-dynamicCarlhttps://www.blogger.com/profile/15013889141640529637noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-36645860682959373262016-02-01T13:35:34.827-05:002016-02-01T13:35:34.827-05:00yes, I think so:
private Stage createStage( String...yes, I think so:<br />private Stage createStage( String fxmlFile, Callback, Object> gcf, String stageTitle, Class sa ) throws IOException {<br /> FXMLLoader aScreenLoader = new FXMLLoader(<br /> sa.getResource( fxmlFile ),<br /> null,<br /> builderFactory,<br /> gcf);<br /><br /> Parent someScreen = (Parent) aScreenLoader.load();<br /> aScreenLoader.getController();<br /> Stage stage = new Stage();<br /> stage.setTitle( stageTitle );<br /> Scene someScene = new Scene(someScreen);<br /> stage.setScene(someScene);<br /><br /> return stage;<br />}Anonymoushttps://www.blogger.com/profile/06678051192859617766noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-89457052373086509782016-02-01T13:10:58.236-05:002016-02-01T13:10:58.236-05:00Are you calling getController() after this line in...Are you calling getController() after this line in the createStage() method?<br /><br />Parent someScreen = (Parent) aScreenLoader.load();<br />Carlhttps://www.blogger.com/profile/15013889141640529637noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-7136482177258554072016-02-01T13:01:16.261-05:002016-02-01T13:01:16.261-05:00Thanks Carl,
how do you use getController()?
Beca...Thanks Carl, <br />how do you use getController()?<br />Because I'm trying to use your function startScreen(ActionEvent evt)<br />but if in the plugin's fxml the controller is set, I receive java.lang.ClassNotFoundException: path.to.ScreenAController<br />pressing the new Button.<br />instead if it is not, pressing that Button the view shows up (but then I can't use that view without a controller).<br />Anonymoushttps://www.blogger.com/profile/06678051192859617766noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-70464505635929220582016-02-01T11:35:45.853-05:002016-02-01T11:35:45.853-05:00Hi,
I don't have the code handy, but this rep...Hi,<br /><br />I don't have the code handy, but this repo has some of the snippets described here: https://bitbucket.org/bekwam/jfxbop-repos-1.<br /><br />Where possible, I like to keep the controllers as isolated as possible. For example, if 2 controllers need to talk it's probably because they're sharing data. In this case, bind UI elements from one or more controllers to the same JavaFX property-based model. Share the model, but not references to each other.<br /><br />I usually use new FXMLLoader(url).load() from a calling class when working with FXML. That way, I can use the getController() method to wire up my main navigation. <br /><br />A lot of people put the FXMLLoader.load() call in a constructor. Then, when you create a new object, you can pass around a reference. I do this with custom components to make the API cleaner. I generally prefer the load() call from a calling class because I use the 470k jar Google Guice to create the instances via dependency injection.<br /><br />Another option is to work entirely in the dependency injection framework, injecting controllers into each other based on Singleton, prototype, or a custom scope.<br /><br />If you have 2 controllers that need to talk to each other, make one of the directions a WeakReference. That way, if the garbage collector can't release one without releasing the other, you'll have provided information about which one should back off.<br /><br />Good luck<br />Carlhttps://www.blogger.com/profile/15013889141640529637noreply@blogger.comtag:blogger.com,1999:blog-6266231010760265271.post-57973741118355347652016-02-01T10:51:02.513-05:002016-02-01T10:51:02.513-05:00thanks Carl, this tutorial is great.
can you pleas...thanks Carl, this tutorial is great.<br />can you please, publish the code? <br />I'd like to check how the controllers are setAnonymoushttps://www.blogger.com/profile/06678051192859617766noreply@blogger.com