ok, I’ll try. but why would the 2nd project need to know the URL of the main app?
I just want the 2nd project to load its own icons from its folder, not the ones from “main”.
With ClassResource, the Application instance is only used in a call to application.getClass().getResourceAsStream(resourceName). You can get the real Application instance e.g. with AbstractComponent.getApplication() from any component as long as both modules are using the same classloader.
If using a ThemeResource, the icons should be in a Vaadin theme, or you need to use theme-relative paths for them (e.g. “…/…/icons/myicon.png” in your case). The resources should be served to browsers from the VAADIN directory or a subdirectory of it, at the same path as your theme. It might make sense to use VAADIN/themes/icons rather than VAADIN/icons.
The forum keeps clearing my “subscribed” flag, so I don’t get email updates from it
what if I don’t have a component in that class?
I tried this
Resource icon = new ClassResource("options_32.png", new Label().getApplication());
setSmallIcon(icon);
but it fails with NPE.
public class AdminModule extends BATModule {
public static final String ID = "admin module";
public static final String MODULE_CATEGORY_ADMIN = "Admin";
public AdminModule(BATModule parent) {
super(parent, "Administration");
// setSmallIcon(new ThemeResource("../../icons/options_32.png"));
Resource icon = new ClassResource("options_32.png", new Label().getApplication());
setSmallIcon(icon);
setLargeIcon(new ThemeResource("../../icons/options_72.png"));
addChild(new UserAdminModule(this));
}
@Override
public String getId() {
return ID;
}
@Override
public String getCategory() {
return MODULE_CATEGORY_ADMIN;
}
}
(BATModule is a POJO class, which is a “building block” for my Vaadin application)
Resource icon = new ClassResource("options_32.png", new Label().getApplication());
You need to attach the Label component into another component that is already attached into Vaadin application. Otherwise AbstractComponent.getApplication() returns null. By attaching I mean calling e.g.
parentComponent.addComponent(myLabel);
I think you need some other mechanism to pass current application instance into your applications. One option is to use some kind of injection or perhaps a ThreadLocal pattern, see this:
ThreadLocal Pattern - Wiki . It also prevents from your API to bloat as you do not have to pass Application instance for your classes manually. You most likely have many places where you need common instances such as current Application instance, not all places have such Vaadin components that have AbstractComponent.getApplication() API available.
I just checked ClassResource and StreamResource implementation and I have to say that Vaadin has a bit stupid API. Both require Application instance just to obtain the correct classloader, I do not know why because classloader should not be defined by Application IMO. More convenient API would be to let the Vaadin user define which classloader is used, it does not have to rely on Application. This is the situation which Alexey has.
As a workaround you can fork ClassResource (or StreamResource) class. It’s very simple and should cause you too much of trouble when Vaadin is updated. Of course, YMMW.
Perhaps a ticket for Vaadin R&D?
Current API has this:
public ClassResource(String resourceName, Application application) {
associatedClass = application.getClass(); ..
We could add also this new API:
public ClassResource(String resourceName, Class myClassFromWhichClassLoaderIsUsed) {
associatedClass = myClassFromWhichClassLoaderIsUsed; ..
Too bad Vaadin needs the Application instance just to “open” the resource to be visible to the end user. So I think the only viable option is to use injection or ThreadLocal, those should work.
Actually, I read the class a bit too quickly and apparently so did Jani. In addition to selecting the classloader, the application instance is also used for two other purposes: for keeping track of registered resources for the application (for client-server communication) and for accessing the context which is needed to create an internal URL for the client to use.
Nevertheless, a valid Application instance is all that is needed, so e.g. accessing the application using a ThreadLocal should be ok.
Yes. Hence I added later the note "
Vaadin needs the Application instance just to “open” the resource to be visible to the end user. ". Still, this resource registration to Applicatoin instance might be done later (lazily), but I’m not sure.
I’ll try to check if there’s more OSGI way to accomplish passing the Application, perhaps on module registration event or such. Currently there’s too much of pain getting Vaadin modules work in OSGI environment, hopefully we can fix this by writing an article.
As a quick and simple response/solution for Alexey’s problem-at-hand, and assuming that BatModule extends a Component of some kind (it looks like it does), simply move the setting of the icons into the attach method. That way, you are guaranteed to have an application!
e.g.
public class AdminModule extends BATModule {
public static final String ID = "admin module";
public static final String MODULE_CATEGORY_ADMIN = "Admin";
public AdminModule(BATModule parent) {
super(parent, "Administration");
addChild(new UserAdminModule(this));
}
@Override
public void attach() {
super.attach();
Resource icon = new ClassResource("options_32.png", getApplication());
setSmallIcon(icon);
setLargeIcon(new ThemeResource("../../icons/options_72.png"));
}
@Override
public String getId() {
return ID;
}
@Override
public String getCategory() {
return MODULE_CATEGORY_ADMIN;
}
}
Charles is right, attach trick should work too. Still, overriding attach is also a bit hacky approach.
I would like to see some kind of common interface for all sub modules requiring Application instance. This interface would require implementing a method that receives Application instance from the module that contains it. Should be reasonably easy because the module that contains Application instance is actually creating the classes of those sub modules that do not have Application instance. Another thing is that do not create Vaadin components on sub module constructor, create them after your interface method has been called and the sub module has the Application instance available.
Hope this helps! Be sure to keep us posted. I have a gut feeling that this issue is not Vaadin specific, there must be bunch of other cases too in which some object is required on sub modules. But, if Vaadin has issues with OSGI, let’s fix it
PS. Even though I suggested using ThreadLocal, I do not like using it between OSGI modules, it just feels wrong. Using it on Vaadin Application instance is cool thing.