Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jansi drowns the entire VM if platform is not supported #55

Closed
michael-o opened this issue Jun 13, 2016 · 12 comments
Closed

Jansi drowns the entire VM if platform is not supported #55

michael-o opened this issue Jun 13, 2016 · 12 comments

Comments

@michael-o
Copy link
Contributor

michael-o commented Jun 13, 2016

We, the Apache Maven team, are currently preparing Maven 3.4 with color logging support. Running a snapshot from Maven master on a platform which is not supported (no so available), drowns the entire VM.
See example for FreeBSD:

$ ./apache-maven-3.4.0-SNAPSHOT/bin/mvn -v

---------------------------------------------------
constituent[0]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/conf/logging/
constituent[1]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/maven-embedder-3.4.0-SNAPSHOT.jar
constituent[2]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/maven-settings-3.4.0-SNAPSHOT.jar
constituent[3]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/plexus-utils-3.0.24.jar
constituent[4]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/maven-core-3.4.0-SNAPSHOT.jar
constituent[5]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/maven-model-3.4.0-SNAPSHOT.jar
constituent[6]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/commons-lang3-3.4.jar
constituent[7]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/maven-settings-builder-3.4.0-SNAPSHOT.jar
constituent[8]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/maven-builder-support-3.4.0-SNAPSHOT.jar
constituent[9]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/plexus-interpolation-1.22.jar
constituent[10]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/plexus-component-annotations-1.6.jar
constituent[11]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/plexus-sec-dispatcher-1.4.jar
constituent[12]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/plexus-cipher-1.7.jar
constituent[13]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/maven-repository-metadata-3.4.0-SNAPSHOT.jar
constituent[14]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/maven-artifact-3.4.0-SNAPSHOT.jar
constituent[15]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/maven-plugin-api-3.4.0-SNAPSHOT.jar
constituent[16]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/org.eclipse.sisu.plexus-0.3.3.jar
constituent[17]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/cdi-api-1.0.jar
constituent[18]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/jsr250-api-1.0.jar
constituent[19]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/javax.inject-1.jar
constituent[20]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/org.eclipse.sisu.inject-0.3.3.jar
constituent[21]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/maven-model-builder-3.4.0-SNAPSHOT.jar
constituent[22]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/guava-19.0.jar
constituent[23]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/aether-api-1.0.2.v20150114.jar
constituent[24]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/maven-aether-provider-3.4.0-SNAPSHOT.jar
constituent[25]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/aether-spi-1.0.2.v20150114.jar
constituent[26]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/aether-util-1.0.2.v20150114.jar
constituent[27]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/aether-impl-1.0.2.v20150114.jar
constituent[28]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/jansi-1.12.jar
constituent[29]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/guice-4.0-no_aop.jar
constituent[30]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/aopalliance-1.0.jar
constituent[31]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/maven-compat-3.4.0-SNAPSHOT.jar
constituent[32]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/wagon-provider-api-2.10.jar
constituent[33]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/slf4j-api-1.7.16.jar
constituent[34]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/commons-cli-1.3.1.jar
constituent[35]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/wagon-http-2.10-shaded.jar
constituent[36]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/wagon-http-shared-2.10.jar
constituent[37]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/jsoup-1.7.2.jar
constituent[38]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/commons-lang-2.6.jar
constituent[39]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/commons-io-2.2.jar
constituent[40]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/wagon-file-2.10.jar
constituent[41]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/aether-connector-basic-1.0.2.v20150114.jar
constituent[42]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/aether-transport-wagon-1.0.2.v20150114.jar
constituent[43]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/gossip-slf4j-2.0.0.jar
constituent[44]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/gossip-core-2.0.0.jar
constituent[45]: file:/net/home/osipovmi/apache-maven-3.4.0-SNAPSHOT/lib/gossip-bootstrap-2.0.0.jar
---------------------------------------------------
Exception in thread "main" java.lang.UnsatisfiedLinkError: Could not load library. Reasons: [no jansi32-1.12 in java.library.path, no jansi-1.12 in java.library.path, no jansi in java.library.path]
        at org.fusesource.hawtjni.runtime.Library.doLoad(Library.java:182)
        at org.fusesource.hawtjni.runtime.Library.load(Library.java:140)
        at org.fusesource.jansi.internal.CLibrary.<clinit>(CLibrary.java:42)
        at org.fusesource.jansi.AnsiConsole.wrapOutputStream(AnsiConsole.java:48)
        at org.fusesource.jansi.AnsiConsole.<clinit>(AnsiConsole.java:38)
        at org.apache.maven.cli.MavenCli.main(MavenCli.java:215)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:606)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
        at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
        at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
        at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)

This doesn't really help because we do not know our users up front and can't have it fail. Jansi shall fall back in no-color mode and emit an error on stderr. With this, it is guaranteed that Maven continues to work without color support. See reference thread. Issues MNG-3507 and MNG-6038.

I already have a fork of fusesource/hawtjni which makes it perfectly compile on FreeBSD 9 and 10 (tests) pending and local snapshots of all components with colored logging on FreeBSD.

FreeBSD is just an example, this will also happen on Solaris, AIX, etc. I assume that isatty() does not fail gracefully?

@hboutemy @jdillon

@hboutemy
Copy link
Collaborator

Christian Schulte prepared #54 PR to fix the issue, by adding a try/catch in init

@michael-o michael-o changed the title Jansi drowns the entire VM if platform is not supported Jansi drowns the entire VM if platform if not supported Jun 13, 2016
@michael-o michael-o changed the title Jansi drowns the entire VM if platform if not supported Jansi drowns the entire VM if platform is not supported Jun 14, 2016
@gslowikowski
Copy link

#54 catches UnsatisfiedLinkError and there is no info that native libraries initialization failed.

@michael-o
Copy link
Contributor Author

@gslowikowski I fully agree here. Not helpful to know that native functions aren't available.

@gslowikowski
Copy link

JAnsi library is used by SBT (directly and transitively by JLine).

I tried to check, how this change influences SBT. Since I have only Windows OS, I did a trick - removed all native libraries from jansi jar file and run SBT with is.

I observer change in behavior.
Latest SBT 0.13.11 version (uses JAnsi 1.11) catches the exception (it's NoClassDefFoundError) and enters fallback mode:

[ERROR] Terminal initialization failed; falling back to unsupported
java.lang.NoClassDefFoundError: Could not initialize class org.fusesource.jansi.internal.Kernel32
    at org.fusesource.jansi.internal.WindowsSupport.getConsoleMode(WindowsSupport.java:50)
...

My prepared SBT version (based on 0.13 branch) used JAnsi 0.13 (with removed native libraries) threw NoClassDefFoundError error

java.lang.NoClassDefFoundError: Could not initialize class org.fusesource.jansi.internal.CLibrary
    at org.fusesource.jansi.AnsiConsole.wrapOutputStream(AnsiConsole.java:72)
    at jline.AnsiWindowsTerminal.detectAnsiSupport(AnsiWindowsTerminal.java:57)
...

but from different context,and what it most important, this error caused SBT termination.

Why don't you rethrow the error and catch it inside Maven code? Something like this:

    static
    {
        try
        {
            out = new PrintStream( wrapOutputStream( system_out ) );
            err = new PrintStream( wrapOutputStream( system_err, STDERR_FILENO ) );
        }
        catch ( final UnsatisfiedLinkError e )
        {
            // Failure loading native library.
            out = system_out;
            err = system_err;
            throw e;
        }
    }

@chirino
Copy link
Member

chirino commented Jun 20, 2016

I don't think it makes sense to error out while loading the AnsiConsole class if it can handle the case where the natives can't be loaded. I do think the PR #54 is doing the right thing.

Actually I think the issue is in wrapOutputStream.. let me take a peek at it later today.

@gslowikowski
Copy link

I just use both Maven and SBT (even wrote a Maven plugin to call SBT like AntRun plugin calls Ant, so I had to learn about SBT bootstraping). Now I saw this issue and think that your fix may break SBT usage of Jansi. Just that. It's up to you, what you decide.

@gslowikowski
Copy link

About showing an error, imagine users asking "why I don't see colors?" If you rethrow the exception, Maven can catch it on its side and print debug nice debug message about the problem.

There are many benefits of that solution:

  • you don't have to care about how/what to print,
  • you are backward compatible,
  • Maven prints this warning the way it fits there.

chirino added a commit that referenced this issue Jun 20, 2016
@chirino
Copy link
Member

chirino commented Jun 20, 2016

Ok I've just committed a change that should handle this better. @gslowikowski could you re-check against a 1.14-SNAPSHOT build?

@michael-o
Copy link
Contributor Author

Just catching Throwable isn't a good idea. I would restrict to UnsatisfiedLinkError.

chirino added a commit that referenced this issue Jun 20, 2016
@chirino
Copy link
Member

chirino commented Jun 20, 2016

made another small change, in the unix case we should just default to passing the ansi codes through we if can't load the native to tell if the user is on a tty.

@gslowikowski
Copy link

I've tested and found something I didn't see before. The first problem (difference in behaviour between 1.11 version used by SBT 0.13.11 and current trunk) is caused by this commit introduced after 1.11.

Previously this block of code caught the error and initialized fallback implementation:

            try {
                return new WindowsAnsiOutputStream(stream);
            } catch (Throwable ignore) {
                // this happens when JNA is not in the path.. or
                // this happens when the stdout is being redirected to a file.
            }

Since 1.12 UnsatisfiedLinkError is thrown from this line:

    public static OutputStream wrapOutputStream(final OutputStream stream) {
        return wrapOutputStream(stream, STDOUT_FILENO);
    }

because reference to STDOUT_FILENO causes CLibrary class initialization which fails. This error here is not handled at all and this leads to the situation where AnsiConsole class is not initialized, what causes NoClassDefFoundError errors later.

Side note:
JLine used by SBT has a method to check if ansi support was properly initialized for Windows

    private static boolean detectAnsiSupport() {
        OutputStream out = AnsiConsole.wrapOutputStream(new ByteArrayOutputStream());
        try {
            out.close();
        }
        catch (Exception e) {
            // ignore;
        }
        return out instanceof WindowsAnsiOutputStream;
    }

This method is only for Windows. It works only if wrapOutputStream handles properly UnsatisfiedLinkError error and JAnsi since version 1.12 doesn't. As I said earlier, I can only test Windows case.

@michael-o
Copy link
Contributor Author

michael-o commented Mar 2, 2020

Cannot reproduce this now for quite some time. Tried on HP-UX recently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants