redhat-developer / rsp-server

A server management protocol based on LSP4J
30 stars 29 forks source link

Issues adding Tomcat 8.5 server to Community Server Connector on SLES SP4 #719

Closed SPMNJ closed 8 months ago

SPMNJ commented 8 months ago

Hi,

Using community-server-connector, we have ran into an issue on a particular service where we are getting unexpected errors when adding servers to it. The OS is SUSE Linux Enterprise Server 15 SP4 in FIPS mode

Trace Message=Received response 'server/createServer - (8)' in 268ms. data=Result: { "status": { "severity": 4, "plugin": "org.jboss.tools.rsp.server", "code": 0, "message": "An unexpected error occurred", "trace": "java.lang.IllegalArgumentException: classPath cannot be null\n\tat org.jboss.tools.rsp.eclipse.jdt.launching.VMRunnerConfiguration.<init>(VMRunnerConfiguration.java:57)\n\tat org.jboss.tools.rsp.server.spi.launchers.AbstractJavaLauncher.configureRunner(AbstractJavaLauncher.java:135)\n\tat org.jboss.tools.rsp.server.spi.launchers.AbstractJavaLauncher.getLaunchCommand(AbstractJavaLauncher.java:77)\n\tat org.jboss.tools.rsp.server.generic.servertype.GenericServerBehavior.setJavaLaunchDependentDefaults(GenericServerBehavior.java:635)\n\tat org.jboss.tools.rsp.server.tomcat.impl.TomcatServerDelegate.setDependentDefaults(TomcatServerDelegate.java:42)\n\tat org.jboss.tools.rsp.server.model.internal.Server.<init>(Server.java:76)\n\tat org.jboss.tools.rsp.server.model.ServerModel.createServer2(ServerModel.java:408)\n\tat org.jboss.tools.rsp.server.model.ServerModel.createServerUnprotected(ServerModel.java:294)\n\tat org.jboss.tools.rsp.server.model.ServerModel.createServer(ServerModel.java:259)\n\tat org.jboss.tools.rsp.server.ServerManagementServerImpl.createServerSync(ServerManagementServerImpl.java:366)\n\tat org.jboss.tools.rsp.server.ServerManagementServerImpl.lambda$11(ServerManagementServerImpl.java:353)\n\tat org.jboss.tools.rsp.server.ServerManagementServerImpl.lambda$37(ServerManagementServerImpl.java:947)\n\tat java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1804)\n\tat java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1796)\n\tat java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373)\n\tat java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182)\n\tat java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655)\n\tat java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622)\n\tat java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165)\n" }, "invalidKeys": [] }

Variables at https://github.com/redhat-developer/vscode-rsp-ui/blob/f89a9d04d5c641e7c421c6cbbccd14e9feb7b0a5/src/serverExplorer.ts#L756 bean { location: "/p/home/spmnj/apache-tomcat-8.5.95", typeCategory: "tomcat.8.5", name: "apache-tomcat-8.5.95", version: "8.5", fullVersion: "8.5.95", serverAdapterTypeId: "org.jboss.ide.eclipse.as.server.tomcat.85", } attributes { id: "Tomcat 8.5", "server.home.dir": "/p/home/spmnj/apache-tomcat-8.5.95", "server.base.dir": "", "vm.install.path": "", } name = "Tomcat 8.5"

Let me know if you want any other debugging information.

robstryker commented 8 months ago

I'll start this off by saying I can't replicate in my normal environment. So... that sucks.

From the trace you posted, it seems there's a null classpath variable. I've verified that if an empty string array is passed in as the classpath on that line, this specific error wouldn't appear (not that it would "work", but just that this error wouldn't show. Maybe the server would simply not be able to start, but that'd be a different error.)

The code using this https://github.com/redhat-developer/rsp-server/blob/dada8f40e218cba530e35bc6098f079fe9c1637d/framework/bundles/org.jboss.tools.rsp.server.spi/src/main/java/org/jboss/tools/rsp/server/spi/launchers/AbstractJavaLauncher.java#L121 here:


        String[] classpath = getClasspath();
        ... etc etc ...
        VMRunnerConfiguration runConfig = new VMRunnerConfiguration(mainType, classpath);

This means getClasspath() must be returning null. But that's... difficult.

The getClasspath() method looks like this in the rsp-server class GenericJavaLauncher: https://github.com/redhat-developer/rsp-server/blob/dada8f40e218cba530e35bc6098f079fe9c1637d/framework/bundles/org.jboss.tools.rsp.server.generic/src/main/java/org/jboss/tools/rsp/server/generic/servertype/launch/GenericJavaLauncher.java#L193


    @Override
    protected String[] getClasspath() {
        String serverHome = getDelegate().getServer().getAttribute(DefaultServerAttributes.SERVER_HOME_DIR,
                (String) null);
        JSONMemento launchProperties = memento.getChild("launchProperties");
        if (launchProperties != null) {
            String javaVersion = getJavaVersion();
            if( javaVersion != null ) {
                String key = getJavaVersionProperty(javaVersion, launchProperties.getNames(), "classpath");
                String cpFromJson = launchProperties.getString(key);
                if (cpFromJson != null && !cpFromJson.isEmpty()) {
                    return getClasspathFromString(serverHome,cpFromJson);
                }
            }
        }
        return null;
    }

So this function can return null, but only if there are no launch properties for the given server type, or getJavaVersion() returns null, or if we can't find a classpath property in the server type definition.

The server type definition can be found here: https://github.com/redhat-developer/rsp-server-community/blob/master/rsp/runtimes/bundles/org.jboss.tools.rsp.server.tomcat/src/main/resources/servers.json#L395

You can see there is a classpath field with a few jars listed. It shouldn't be returning null here.

It will also return null if getJavaVersion() is null. We only use the java version to help find a more customized classpath. Some server types require different classpath or command line arguments depending on what java version is being used to launch it, and we use the java version to discover that. Whether we should return null on a missing java version is debatable. On the one hand, if we can't discover the java version of the currently running VM, we have bigger issues ;) On the other hand, we don't actually NEED the java version to discover arguments and classpaths in most cases (but do in some, I guess). Anyway, here's the getJavaVersion() implementation:


    protected String getJavaVersion() {
        IVMInstall vmi = getVMInstall(getDelegate());
        String javaVersion = vmi.getJavaVersion();
        return javaVersion;
    }

If no vm was found, this would NPE, and we'd see a different error. So we can safely assume a vm install has been found. But vmi.getJavaVersion() is a clone of an eclipse implementation class, and can also return null (though I've 100% never ever seen that before, since it's checking the version of the java running the RSP itself...)

At this point, the most logical answer is that this eclipse code doesn't seem to work with your environment. Here's what the code is doing, just in case anything looks suspicious:


    @Override
    public String getJavaVersion() {
        StandardVMType installType = (StandardVMType) getVMInstallType();
        File installLocation = getInstallLocation();
        if (installLocation != null) {
            File executable = getJavaExecutable();
            if (executable != null) {
                String vmVersion = installType.getVMVersion(installLocation, executable);
                // strip off extra info
                StringBuffer version = new StringBuffer();
                for (int i = 0; i < vmVersion.length(); i++) {
                    char ch = vmVersion.charAt(i);
                    if (Character.isDigit(ch) || ch == '.') {
                        version.append(ch);
                    } else {
                        break;
                    }
                }
                if (version.length() > 0) {
                    return version.toString();
                }
            }
        }
        return null;
    }

This function returns null if the vm install doesn't have a n install location, or it does but we can't find the java executable. In all other cases it should likely return something, even if it's just an empty string.

What can you tell me about the environment and what VM is used when launching the RSP? Are you using any mapped drives or anything like that? Or is it all on one machine?

SPMNJ commented 8 months ago

Hey Rob, Thanks so much for the fast response,

I will say its not a very fun system. Mapped drives, using the Lustre file system, OpenJDK 64-Bit Server VM (build 18.0.1+10-24, mixed mode, sharing) and best of all managed by a 3rd party. 😞 Worst problem apart about this server is that it updated from SP3 to 4 recently and a lot of things broke. (Everything works great on a separate SLES SP3 but its being decommissioned)

I'm going to try that function on a new java project and possibly run the source code on debug on the server and see what the fuss is all about.

Btw thank you so much for the extension and help its helped a ton and an upgrade! I will let you know possibly next week if we discover anything.