fesch / Structorizer.Desktop

Structorizer is a little tool which you can use to create Nassi-Schneiderman Diagrams (NSD).
https://structorizer.fisch.lu
GNU General Public License v3.0
65 stars 20 forks source link

Exception in thread "main" java.lang.NoClassDefFoundError: com/apple/eawt/ApplicationListener (issues with OpenJDK + makeBigJar script) #537

Closed GitMensch closed 6 years ago

GitMensch commented 6 years ago

Steps I did locally:

C:\dev\Structorizer codemanyak>set JAVA_HOME=C:\Program Files\Java\java-1.8.0-openjdk-1.8.0.141-1.b16

C:\dev\Structorizer codemanyak>set JDK_HOME=%JAVA_HOME%

C:\dev\Structorizer codemanyak>set JRE_HOME=%JAVA_HOME%\jre

C:\dev\Structorizer codemanyak>set CLASSPATH=.;%JAVA_HOME%\lib;%JRE_HOME%\lib

C:\dev\Structorizer codemanyak>set PATH=%PATH%;%JAVA_HOME%\bin

C:\dev\Structorizer codemanyak>"C:\Program Files\Git\bin\bash.exe"

simon@PC MINGW64 /c/dev/Structorizer codemanyak
$ which javac
/c/Program Files/Java/java-1.8.0-openjdk-1.8.0.141-1.b16/bin/javac

simon@PC MINGW64 /c/dev/Structorizer codemanyak
$ ./makeBigJar
/c/dev/Structorizer codemanyak/jar /c/dev/Structorizer codemanyak
Decompressing jars ...
/c/dev/Structorizer codemanyak
Copying resources
Create a new archive
Running Structorizer from all-in-one jar
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: com/apple/eawt/ApplicationListener
        at java.lang.Class.getDeclaredMethods0(Native Method)
        at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
        at java.lang.Class.privateGetMethodRecursive(Unknown Source)
        at java.lang.Class.getMethod0(Unknown Source)
        at java.lang.Class.getMethod(Unknown Source)
        at sun.launcher.LauncherHelper.validateMainClass(Unknown Source)
        at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.apple.eawt.ApplicationListener
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 7 more

C:\dev\Structorizer codemanyak>
codemanyak commented 6 years ago

Oops, I have never put my fingers in the Apple stuff. Can you make sure it's not the blank in the pathname that causes the trouble?

GitMensch commented 6 years ago

Can you make sure it's not the blank in the pathname that causes the trouble?

Rechecked with c:\dev\Structorizer - still the same. Note: also happens when running makeJar

... and when running makeStructorizer in a clean checkout:

$ ./makeStructorizer
Removing .class files
Changing into src directory
/c/dev/Structorizer/src /c/dev/Structorizer
Compiling non-dependant classes
lu\fisch\structorizer\generators\BASHGenerator.java:122: error: package lu.fisch.structorizer.elements does not exist
import lu.fisch.structorizer.elements.Alternative;
                                     ^

What did I miss?!?

fesch commented 6 years ago

My script for creating the source package uploaded to my site contains these lines in order to clean if from the Apple specific code:

echo "Removing MAC specific files"
rm source/src/Structorizer.java
rm source/src/Structorizer.clean
rm source/lib/AppleJavaExtensions.jar

echo "Copy clean launcher class"
cp -f src/Structorizer.clean source/src/Structorizer.java
GitMensch commented 6 years ago

@fesch Thank you for the note. I could rearrange the currents scripts and do a PR. Please tell me what you think of the following:

new script "prepareDist" (with optional parameters "mac" / "jar" or "bigjar")

fesch commented 6 years ago

Yes, I think this will be a fine solution for everyone who want's to make his own release.

I have no idea what startStructorizerAgain was written for. My name is inside, yes, but I can't even remind having written it. :-(

GitMensch commented 6 years ago

@fesch Can I remove startStructorizerAgain? Do you have other scripts that may be added into this process (ideally you can do everything you need from a clean checkout)?

fesch commented 6 years ago

I have no problem if it is being removed. The only one I personnaly need for the release, is the "sign" script, but that one is not on GitHub as it contains sensible data ;)

codemanyak commented 6 years ago

I have never needed nor used startStructorizerAgain, either, if you must know. So I guess I will hardly miss it in future. ;-)

KimBrodowski commented 6 years ago

The repo contains a couple of scripts now whose sole purpose it is to prepare and build from source. Any chance migrating this to a Makefile? This would greatly reduce redundancies as well. Not quite sure how Makefile support on Mac is, but generally speaking it's the de-facto standard in the Linux/Unix world.

I'm facing similar issues while compiling on Linux. I assume the fix above will clear that up. since Mac specific code is the issue for me as well.

Structorizer.java:329: error: package com.apple.eawt does not exist
            com.apple.eawt.Application application = com.apple.eawt.Application.getApplication();
                          ^
Structorizer.java:329: error: package com.apple.eawt does not exist
            com.apple.eawt.Application application = com.apple.eawt.Application.getApplication();
                                                                   ^
Structorizer.java:334: error: package com.apple.eawt does not exist
                application.addApplicationListener(new com.apple.eawt.ApplicationAdapter() {
                                                                     ^
Note: Some input files use or override a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
3 errors
fesch commented 6 years ago

As said before, the file Structorizer.java should be replaced by the file Structorizer.clean if you want to build Structorizer without the Apple extensions. Unfortunately I did not find a better way to do this, as in Java we have no real equivalent for C's #ifdef compiler directive.

fesch commented 6 years ago

I'll open a new issue for the Makeflile request.

GitMensch commented 6 years ago

@fesch Can I suggest to swap the files? Obviously they lead to confusion and something like Structorizer.java -> Structorizer.apple.java Structorizer.clean -> Structorizer.java

Would solve the issue.

fesch commented 6 years ago

Yes, I could do that, too. Or I find a way to enable Apple support by cmd and disable it afterwards. Maybe with a little sed command will do the trick. I have some ideas and will test them later on ...

fesch commented 6 years ago

I have completely removed all files except for Structorizer.java. When calling makeStructorizer, the Apple specific lines will be disabled automatically.

Maybe I should also add a big comment at the top of the file referring to the specific line if someone would like to comment out this part of the code by himself?

fesch commented 6 years ago

... of maybe I could use reflection to make the desired calls?

codemanyak commented 6 years ago

@fesch

When calling makeStructorizer, the Apple specific lines will be disabled automatically.

I wonder how you create the Apple variant of the product - obviously not with the script makeStructorizer since it doesn't expect a parameter suppressing the code modification? And how is Structorizer.java restored after makeStructorizer? Just by reverting the code?

The next thing I don't get is: In line 58 (of "makeStructorizer") you refer to Structorizer.Applet.java though you have removed that file...

fesch commented 6 years ago

The code in the repository will the Apple specific code always enabled. I don't use the makeStructorizer script neither for compiling nor for packaging. I don't use it at all!

I compile using NetBeans immediately. Then I launch another script, which is obviously not in the repository as it contains sensible data, to generate all the packages and documentation and upload it to the various servers.

Just realizing that I removed a file I should not have .... tzz

codemanyak commented 6 years ago

@fesch I don't use this script either, instead I let Eclipse do the job, and having the Apple extensions in the project, there is no complaint, you know. Of course, they are sort of dead weight in the jar whilst you don't work on a Mac ;-)

So perhaps your proposal to use reflection may really be the simplest way to handle this. We already use reflection for other purposes (like locales) in the code, hence it wouldn't be a new breach...

GitMensch commented 6 years ago

Go Reflection Go

codemanyak commented 6 years ago

The major difficulty is to find some equivalent to this subclass definition on the fly with reflection:

    application.addApplicationListener(new com.apple.eawt.ApplicationAdapter() {
        public void handleAbout(com.apple.eawt.ApplicationEvent e) {
            mainform.diagram.aboutNSD();
            e.setHandled(true);
        }
        // ...
    });

I tried with something like:

        if (System.getProperty("os.name").toLowerCase().startsWith("mac os x"))
    {
            try
            {
                // Test for availability of the named class (will throw an exception otherwise)
                Class.forName("com.apple.eawt.Application");

                System.setProperty("apple.laf.useScreenMenuBar", "true");
                System.setProperty("apple.awt.graphics.UseQuartz", "true");

                // FIXME The system does not necessarily provide a Java compiler
                JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
                URL url = Structorizer.class.getClassLoader().getResource("StructorizerApplicationAdapter.java.txt");
            compiler.run(null, null, null, url.getPath());
            // Test whether the compilation has worked
            Class<?> adapterClass = Class.forName("StructorizerApplicationAdapter");

            Class<?> applClass = Class.forName("com.apple.eawt.Application");
            Object application = applClass.newInstance();

            //application.setEnabledPreferencesMenu(true);
            Method method = applClass.getMethod("setEnabledPreferencesMenu", new Class[]{boolean.class});
            method.invoke(application, new Object[]{true});

            //application.addApplicationListener(new com.apple.eawt.ApplicationAdapter() { ...
            Object adapter = adapterClass.newInstance();
            //adapter.setMainform(mainform);
            method = adapterClass.getMethod("setMainform", new Class[]{Mainform.class});
            method.invoke(adapter, new Object[]{mainform});

            method = applClass.getMethod("addApplicationListener", new Class[]{Class.forName("com.apple.eawt.ApplicationAdapter")});
            method.invoke(application, new Object[]{adapter});  
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

StructorizerApplicationAdapter.java.txt

... but the trouble is that usually the system doesn't provide a Java compiler - so is bound to fail :-(.

codemanyak commented 6 years ago

I'm afraid there is no way to achieve the effect of conditional compilation while we have to use a subclass of a class the existence of which is not clear at compile time. If I'm right then we can forget about reflection here. (And by the way, a compilation at runtime is certainly not recommendable anyway, from both a performance and a security point of view. I had just tried to see if that way is possible at all.)

GitMensch commented 6 years ago

Bad hack: simply add an "empty" com.apple.eawt.* package and have this in classpath exclusion for Apple Systems. Or a little bit less hacky: move the apple code to a new source file that has a static function as entry. Then have two versions of this java class, one containing nothing the other one do the current calls. Depending on the classpath setting the full one (and the additional apple-dependencies) are used or the other (that may raise a warning if apple os detected).

codemanyak commented 6 years ago

@GitMensch All these approaches mean to solve it in the configuration management, not in the code. Of course the code should support it in an elegant way, say some factories, mock interfaces, class name providers for reflection or whatsoever, to unify the Structorizer.java (which makes sense) but at some place or other in the project a subclass of an optionally available Apple-specific class must be defined and this code will blow the compilation in absence of the underlying Apple package. Some parameterized script or two versions of a project file or so will be necessary.

There could e.g. be some class ApplicationFactory providing some Application object for certain name, which responds to a method (may be a dummy or not) configureFor(Mainform mf) allowing the following code:

        if (System.getProperty("os.name").toLowerCase().startsWith("mac os x"))
    {
            ApplicationFactory.getApplication("apple").configureFor(mainform);
    } 
GitMensch commented 6 years ago

Some parameterized script or two versions of a project file or so will be necessary.

Yes, one needs the additional dependencies, the other one needs a classpath exclusion. It would be nice if you could provide the necessary code changes, the changes in the build scripts are then minimal, project files must be inspected (I don't know if Netbeans and Eclipse offer different configurations in the same project), or otherwise duplicated.

codemanyak commented 6 years ago

@GitMensch wrote:

It would be nice if you could provide the necessary code changes, the changes in the build scripts are then minimal, [...]

Well, I just set up a pull request https://github.com/fesch/Structorizer.Desktop/pull/593 addressing this. It may be tested in branch fesch/Structorizer.Desktop/make (where I directed it on purpose in order to keep the master branch clean of half-boiled code) if the pull would be accepted. With the changes in Structorizer.java I averted the impact of the sed command in makeStructorizer effectively by removing the marker comment. So the script or project file configuration is then up to you.

GitMensch commented 6 years ago

@codemanyak wrote:

Well, I just set up a pull request #593 addressing this. It may be tested in branch fesch/Structorizer.Desktop/make (where I directed it on purpose in order to keep the master branch clean of half-boiled code) if the pull would be accepted. With the changes in Structorizer.java I averted the impact of the sed command in makeStructorizer effectively by removing the marker comment.

You did more than that. With the new setup it doesn't matter if the AppleExtensions are not available as jar or not any more (both on compilation and on runtime) as long as the OS doesn't match osx. That is really good. I assume (did not test) that also everything will work (just without the extensions) if the jar/class is missing on osx.

So the script or project file configuration is then up to you.

Did, see PR #599.

fesch commented 5 years ago

Maybe this will help ...

https://developer.apple.com/library/archive/samplecode/OSXAdapter/Listings/src_MyApp_java.html#//apple_ref/doc/uid/DTS10000685-src_MyApp_java-DontLinkElementID_5

codemanyak commented 5 years ago

@fesch

Maybe this will help ...

I guess the OSXAdapter has the potential to facilitate your release process? I might have a deeper look into it (somewhat later). Unfortunately I don't have an OS X development environment to test such an approach. As far as I can see, Structorizer used to delegate its entire menu to the OS X application menu (in contrast to that myApp example), didn't it?

fesch commented 5 years ago

Yes, I've done the integration ... no push yet. I have developed a new launcher as replacement fir the JWS which will disappear quite soon ...

GitMensch commented 5 years ago

@fesch wrote:

Yes, I've done the integration ... no push yet.

So where do we stand here now?

codemanyak commented 5 years ago

So where do we stand here now?

I haven't done anything about it so far, and I am not even sure whether I should have. I got the impression that Bob is caring about it and I don't feel any stress here.