This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 53898

Summary: Enhance module system to support external libraries
Product: platform Reporter: Jaroslav Tulach <jtulach>
Component: Module SystemAssignee: Jesse Glick <jglick>
Status: RESOLVED WONTFIX    
Severity: blocker CC: anebuzelsky, dkonecny, jchalupa, jglick, jrechtacek, jtulach, ludo, mkrauskopf, mstevens, nassern, pjiricka, rkubacki, santhosh, ttran, yardus
Priority: P3 Keywords: API
Version: 4.x   
Hardware: All   
OS: All   
Issue Type: ENHANCEMENT Exception Reporter:
Bug Depends on:    
Bug Blocks: 59555    
Attachments: Proposed enhancement in module system
Support for external manifest

Description Jaroslav Tulach 2005-01-24 15:04:23 UTC
Basic issue is that a netbeans module can depend
on external libraries, only if they are positioned
under a relative path to this module, so under the
modules directory.

The netbeans convention is to use modules/ext or
modules/docs. The is a std solution also provided
by J2SE jars with the manifest class-path element:
only relative sub-paths (not ../../ allowed or
absolute paths)

I am trying to find a way to ease the process of
developing nb modules that need at runtime
external jars. One example is for the App Server
plugin than needs the JSR88 implementation as well
as the admin apis implementation at run time from
the app server.

Dynamic class loading is possible, but technically
hard (especially to debug) , and might frightened
people to develop new plugins for jboss, oracle,
bea,... for example unless they copy possilby huge
jar files under modules/ext...

A solution could then be applied to bigger stacks
like JES...
Comment 1 Jaroslav Tulach 2005-01-24 15:05:18 UTC
Without even thinking about usecases and requirements here is one
solution [question is what it is solution to, but I am sure it is
solution]:

Each module JAR file comes with a descriptive XML file in
config/Modules/ directory. It defines the state of the module
(enabled/disable), and type (regular, autoload, eager). This XML file
could be extended with voluntary element describing locations of extra
libraries:

<extra-cp>
  <path location="c:/Program Files/AppServer8/jars/jsr88.jar" />
  <path location="..." />
</extra-cp>

This extra classpath would be read and interprested by the module
system and would be included in the module's runtime classloader.

The cobundle installer would install appserver8 and netbeans and then
modify the files in nb4.1/config/Modules/ to include this extra-cp
element for modules that need to refer to libraries provided by
appserver8.

The UI of Modules node could be extended to allow the user to edit
this path - especially for disabled modules due to defined dependency
on a specific package: javax.help[Help] could allow the user to locate
a jar file on disk, check if the missing classfile is provided and if
so modify the $userdir/config/Modules/... with extra-cp element, so
from now on, the javahelp would get inserted into modules classpath
and the module could be enabled.
Comment 2 Jaroslav Tulach 2005-01-24 15:10:07 UTC
Created attachment 19906 [details]
Proposed enhancement in module system
Comment 3 Jaroslav Tulach 2005-01-24 15:14:55 UTC
Reviewers, I'd like to get a review. Fast one would be good, as there
is non-zero probability Ludo will insist on a solution to this problem
to be implemented for 4.1.

Ludo, I'd like you to try this patch and confirm that you can use
<extra-cp><path location="..."/></extra-cp> to refer to external
libraries and that it works correctly.

Jesse, can you please review the changes in code and the test and
suggest improvements? I am glad I made it work, but I am sure the code
could look nicer. Also please tell me what documentation/manifest
files to update. Increase version of openide? Etc... Thanks.
Comment 4 Jesse Glick 2005-01-24 16:22:39 UTC
Don't like this so much since config/Modules/*.xml is supposed to be
distributed with an NBM and be installation-neutral. This mixes
installation details into the module configuration, where it does not
really belong IMHO.

I would rather suggest a somewhat different approach which is more
general (and probably simpler to implement):

1. Continue to use Class-Path in manifests to declare external
libraries, e.g.

Class-Path: ext/jsr88.jar

However make sure that Module interprets Class-Path using
InstalledFileLocator in case the direct interpretation (URI relative
to module JAR's dir) fails to match. This is what we already do for
e.g. localizing/branding JARs.

2. Modify the core InstalledFileLocatorImpl to search the installation
directories for files named e.g. "links/$code-name-base.properties"
(so in this case e.g. "links/com-sun-appserverplugin.properties"). The
keys are logical paths, the values are physical paths (either
absolute, or relative to the installation directory). E.g.

modules/ext/jsr88.jar=c:\\Program Files\\AppServer8\\jars\\jsr88.jar

The nice thing here is that you can have a regular NBM, which really
works (can include jsr88.jar), and the module can be tested as it
comes in that NBM (or in reload mode etc.); when you want to produce
an installer that works with an external program installation, all it
needs to do is write one properties file (no need to modify existing
XML files etc.).

What do you think? Should be very simple to do. 
Comment 5 _ ludo 2005-01-24 17:45:34 UTC
Jesse, I like this proposal better. It's great both for internal work,
and the installer team can further customize the installation without
changing existing code/jars...

One tech question now around duplicated class loaders:

let's say 2 modules use ext/jsr88.jar  (symbolic name)

would you emit a warning is the real jar is the same for the 2 modules?
I think this is currently done for regular extension jars.

Comment 6 _ ludo 2005-01-24 17:51:06 UTC
Also would we support relative links like:

modules/ext/jsr88.jar=../AppServer8/jars/jsr88.jar

This would help installer team to better handle cobundles of products
with NetBeans, in a platform independant way, assuming the bundled
products are installed in a known area, for ex:

C:\\MegaBundle\\Netbeans
C:\\MegaBundle\\AppServer
C:\\MegaBundle\\WebServer
C:\\MegaBundle\\Tomcat
...

Comment 7 Jesse Glick 2005-01-24 18:14:56 UTC
Re. duplicated JARs: yes, I think the existing warning about
duplicating JARs in different class loaders should be kept and
continue to apply to actual file path. That should probably work with
no additional code changes. See Javadoc for
org.netbeans.core.modules.Module.extensionOwners.

Re. permitting relative paths to use "../" and thus jump out of the
cluster directory into a neighboring directory: I don't see why not. I
guess the example is not quite right, since you would really have e.g.

---%<--- /opt/NetBeans/platform5/links/com-sun-asplugin.properties
# Points to /opt/AS8/jars/jsr88.jar
# relative to /opt/NetBeans/platform5:
modules/ext/jsr88.jar=../../AS8/jars/jsr88.jar
---%<---

i.e. the base directory used for resolving relative paths is the
cluster. (Technically: ${netbeans.home}, ${netbeans.user}, or an entry
in ${netbeans.dirs}: the same as the roots used by
InstalledFileLocatorImpl.)
Comment 8 Matthew Stevens 2006-10-31 21:40:21 UTC
I think that this requirement may become very important in the context of
scripting support since the library dependencies are crucial.  We do not want to
be getting into a situation of X copies of RHino and jRUby and others.  It will
be helpful to have a current assessment/swag as to what the effort would be
today wrt to Nb 6.0

I realize that even consideration of this would have no bearing on something
like near term Phobos Tools which are targeting NetBeans 5.5 release.
Comment 9 Petr Jiricka 2006-11-01 08:08:02 UTC
Commenting on the solutions suggested by Jesse and Jarda: One additional aspect
that I believe needs to be addressed is that it is not sufficient to provide the
external jar path only by the installer - it must be possible to provide this
path at IDE runtime using UI provided by the module. For example, in an
installation without any appserver instance registered, the user can register
the server using the Server Manager (generic UI in module manager is not
sufficient). This also means that the module would be enabled or disabled based
on whether the external jar path is defined. Also, modules would need to have a
way to programatically set the external jar path.
Comment 10 Jesse Glick 2006-11-02 00:30:38 UTC
Petr - this feature should not be used for dynamically registered external
libraries. That is best handled in your code, using ClassLoader's to split
module code across a set of interfaces. The feature should only be used for
libraries which we could have bundled statically as Class-Path extensions but
which we would prefer to use from a predefined external location known to
installation/packaging tools. For example, the Ant module permits the user to
select a different version of Ant to run. In such a case it is inappropriate to
use this feature since only the Ant module, not the module system, knows how to
properly handle runtime changes in the Ant installation (certainly the Ant
module should not be reloaded after such a change).

Also I would not propose any UI in the Module Manager! This feature is designed
for invisible use e.g. as part of an RPM/deb/solpkg/MSI package, or from a
standalone installer. The external library linkages should never be changed
while the IDE is running.
Comment 11 Petr Jiricka 2006-11-02 12:44:58 UTC
If that's the case, then that means that this enhancement request will have
limited usefulness, as in many real world cases (Ant module, and I believe also
the Appserver plugin), we will need to continue to use the current approach with
custom classloaders handcoded in the module. And that may well be the right
solution. 

To return back to the motivation, do we have examples of cases when providing
the external classpath statically at installation/startup time is sufficient,
and it is not necessary to change this path through UI at IDE runtime?
Comment 12 Jesse Glick 2006-11-02 17:59:29 UTC
An example of a static use case would be JavaHelp - if it is installed on the
system in a reliable location and we are providing NB as a package using the
same package system (e.g. RPM) we would prefer to simply use the existing
installation. We do not permit the user to configure the JH path now and so this
feature is applicable. Generally - the feature should be useful where a library
is used as part of the IDE's functionality, but not when the library may be used
as part of the user's application.
Comment 13 Jaroslav Tulach 2008-05-03 01:50:23 UTC
Let's start once again. I've just met Ludo and he reminding me that this is still opened issue. That is why I coded 
the "external" manifest support. As far as I know this is exactly what our competition does to solve inclusion of JARs 
which are not native or are located elsewhere. 

My diff is missing documentation in apichanges and arch, but otherwise I consider it done. It would be nice to reuse 
it in 3rd party library wrapper one day, but that is definitely not a blocker for Ludo. I know I completely ignore all 
the above suggested tricks with properties, but I do not like them and I will not implement them myself.

If there are no objections, I'll polish the documentation and integrate sometime in second week of May, when J1 is 
over.
Comment 14 Jaroslav Tulach 2008-05-03 01:51:15 UTC
Created attachment 60998 [details]
Support for external manifest
Comment 15 _ ludo 2008-05-03 02:23:40 UTC
Great to see some activity there.
I guess we need to test it with a real case: either GlassFish, or even better the Nb DTrace plugin that needs a jar from
/usr/share/lib/java area...
Comment 16 Jesse Glick 2008-05-12 16:18:59 UTC
[JG01] A much simpler (and to my mind preferable) patch would just be to alter the code in the module system which
interprets Class-Path to permit an absolute path (or URL?) to be specified. No other changes to an NBM would be needed:
just change Class-Path to a full path on disk, and do not unpack the modules/ext/*.jar (or whatever). The build harness
ought to enforce simple relative paths in <class-path-extension>s, since a switch to an absolute path should only be
done as part of an installer or native package.

I would also like to take this opportunity to remind people that the applicability of such an RFE is limited to cases in
which runtime or user-directed customization of the library in question is unnecessary and inappropriate. For any kind
of dynamic behavior it is necessary to use a custom ClassLoader, which has always been possible without any support from
the module system.

I agree that one or more concrete use cases would be required in order to properly evaluate any solution.
Comment 17 Nasser Nouri 2008-05-28 22:43:09 UTC
The new DTrace gui plugin has dependencies on an external jar file (/usr/share/lib/java/dtrace.jar)
which cannot be bundled with the nbm file. The /usr/share/lib/java/dtrace.jar file is shipped with Solaris OS.
We should allow the platform specific plugins such as the DTrace plugin, to specify the absolute pathname 
to an external jar file via the module Class-Path. 
Comment 18 Petr Jiricka 2008-06-02 12:28:31 UTC
> We should allow the platform specific plugins such as the DTrace plugin, to specify the absolute pathname

What would be the behavior on other platforms? Would the module be disabled? If so, would the user be notified about the
module being disabled? If not, would it throw ClassNotFoundExceptions in the user's face or do something else?

My feeling is that this is not an API-only enhancement, there is also a UI/behavior aspect for which we need to have a spec.
Comment 19 J Bachorik 2008-10-09 09:47:14 UTC
pjiricka: 
As for the DTrace case it's possible to define OS dependency for that module. Then the module would be disabled on other
platforms than Solaris - and it would show the standard "The module blablabla is disabled blablabla..." dialog. So no
CNFE in this case.
Comment 20 Nasser Nouri 2008-11-20 02:30:22 UTC
What is the status of this feature? The DTrace GUI Plug-in can use this feature if it is ready.
Comment 21 Jesse Glick 2008-11-20 20:05:08 UTC
No one is working on this, and it is not clear whether anyone will. You don't *need* it for the DTrace plugin since you
can always load /usr/share/lib/java/dtrace.jar dynamically using URLClassLoader, rather than using Class-Path.
Comment 22 Jaroslav Tulach 2008-11-21 13:36:23 UTC
As author of the last patch
http://www.netbeans.org/nonav/issues/showattachment.cgi/60998/X.diff
I think I can donate some time and integrate it. I am not sure if Jesse's yesterday's comment means that he wants to 
block the change or he just does not want to do it himself. Thus, I'll delay the integration till Monday.
Comment 23 Jesse Glick 2008-11-21 16:09:20 UTC
I still prefer the solution suggested in JG01. Implementation would be much simpler (merely the removal of three lines
of code from StandardModule.java), and I think usage is more intuitive: rather than

Class-Path: ext/dtrace.jar

you have

Class-Path: /usr/share/lib/java/dtrace.jar


[JG02] As I've said before, I'm not fully convinced it is even desirable to implement this RFE. I would urge module
authors to instead consider dynamic loading of the external JAR, which offers much more flexibility: you can select the
external JAR location from among several alternatives, display a polite dialog in case it is missing (or even offer to
install the native package which contains it), deal with version skew intelligently, etc.

Another alternative would be a post-install action in the NBM which would locate and validate the external JAR, then
create a symlink to it from .../modules/ext/*.jar.
Comment 24 Nasser Nouri 2008-11-21 19:57:40 UTC
The post-install solution works fine as long as the plugin is installed from the NetBeans Update Center. In fact, the
DTrace GUI uses post-install to copy the dtrace.jar file to the module/ext directory. 

However, the post-install solution doesn't work for the modules that have dependencies on a platform specific jar file
and  they are already installed/integrated with NetBeans.

For the URLClassLoader soultion, I would like to know whether your suggestion works with both the plugin modules and for
the modules that are already integrated with NetBeans. I would appreciate it if you point me to the modules and plugins
that are using URLClassLoader to load a platform specific jar file. 

Comment 25 Jesse Glick 2008-11-21 20:13:57 UTC
"the post-install solution doesn't work for the modules that have dependencies on a platform specific jar file and they
are already installed/integrated with NetBeans" - obviously if the module is installed in some other way, not from NBM,
then you would use that mechanism's installation hooks. I don't know about Solaris, but Linux packages can directly
specify symlinks as part of the content of the package, which would trivially satisfy your requirement: your plugin
package would depend on both the DTrace package and some NB package, and include a link from .../modules/ext/dtrace.jar
-> /usr/.../dtrace.jar. Of course Linux packages can also run scripts during installation, in case a static symlink was
not flexible enough.

"For the URLClassLoader solution, I would like to know whether your suggestion works with both the plugin modules and
for the modules that are already integrated with NetBeans." - I'm not sure exactly what the distinction is in your mind,
but it should make no difference.

"I would appreciate it if you point me to the modules and plugins that are using URLClassLoader to load a platform
specific jar file." - I don't know about "platform-specific" JAR files, but the Ant integration module uses (a subtype
of) URLCL to load ant.jar from an arbitrary location. There is no special NB API involved here, just plain Java
programming: you create the class loader, you load some entry point class, you call it using reflection. If there are a
lot of entry points, generally it would be easier to create an interface in the module representing them all, an impl in
a separate JAR which can link against the foreign JAR, then load the impl plus the foreign JAR together, instantiate the
impl, cast to the interface, and continue from there.
Comment 26 Jaroslav Tulach 2008-11-21 22:51:52 UTC
Re. Solution JG01 requires repackaging of a JAR file. This is too complicated for my taste. Better to generate simple 
text file, imho.

Re. JG02 own classloading is imho needlessly complicated too. Imho, it is much simpler to generate dtrace.mf with
OpenIDE-Module: com.sun.dtrace
and into config/Modules/com-sun-dtrace.xml add
<manifest>dtrace.mf</manifest>
but if you really do not want me to integrate this change, then remove the keyword by Monday. Thanks.

Comment 27 Jesse Glick 2008-11-21 23:04:51 UTC
You do not need to repackage the JAR file if you write

Class-Path: /usr/share/lib/java/dtrace.jar

into ${basedir}/manifest.mf to begin with. You would also need

cp.extra=/usr/share/lib/java/dtrace.jar

to compile. This assumes that you always want to run the module using the external JAR, i.e. that it is designed this
way from the beginning.

Anyway editing the manifest of a packaged JAR is quite trivial. In Emacs, just C-x C-f, select the JAR, move down to
META-INF/MANIFEST.MF, press f, edit, C-s to save, and you are done. Seems simpler & easier than extracting the manifest,
saving it somewhere, editing it, then editing a separate XML file to point to the copy.

As to class loading, it is not "needlessly" complicated; it is a little more work, but in return you get a much more
flexible and robust module, which can safely be installed without precondition onto any system, and decide at runtime
how to handle missing external dependencies. (If your module is installed only by nonstandard mechanisms such as native
packages, then you do not need help from the NB module system anyway, as you can use simple symlinks.)

And any such inconveniences seem to me preferable to introducing a new API and nontrivial implementation into the module
system. So overall I would prefer the patch not be integrated.
Comment 28 Nasser Nouri 2008-11-21 23:46:16 UTC
I have already tried most of your suggestions few months ago which includes: 

* Creating the link from the module/ext to the /usr/.../dtrace.jar
* Modifying the manifest file manually
* Using the URLClassLoader

None of them worked for me with the exception of the post-install solution. Which means copying the /usr/.../dtrace.jar
file into the module/ext directory.

It is possible that I didn't implement them correctly and that is why I would like you to be more specific and point me
to the modules that are already using any of those solutions successfully.
Comment 29 Jesse Glick 2008-11-24 19:11:39 UTC
I already directed you to the Ant integration module which uses URLClassLoader (o.apache.tools.ant.module in NB sources).
Comment 30 Jesse Glick 2009-03-27 16:23:20 UTC
I don't see any compelling need that can't be met either by installation/packaging systems. Providing a feature that
would be implemented by the core module system seems inappropriate, since an NBM cannot portably refer to a possibly
nonexistent external file anyway.

Dynamic class loading and communication through stub interfaces is definitely the preferable technique. If there is a
need for some support API that would make it easier to manage the class loaders, that is certainly possible, though I
suspect there is not much to write that is not specific to the situation.