HP-UX: B.11.31 U ia64
Java: 7 or 8
Spring-boot: 1.5.3.RELEASE
Tomcat: 8.5.14
Update: Looks like a problem with Java JNI/JVM under HP-UX as we do not get this error under RHEL
org.apache.tomcat.util.net.NioEndpoint$Acceptor run SEVERE: Socket accept failed java.nio.channels.NotYetBoundException
I have a spring-boot application which uses an embedded tomcat server. The JAR has been repackaged as myapp-exec.jar
using spring-boot:repackage so that it can be executed from the command line using java -jar, and runs successfully as a stand-alone unit for testing purposes only. The same code, available in the original JAR myapp.jar
without all the BOOT-INF and spring-boot details, is designed to be executed in production by a JVM embedded in a C++ application. Due to the repackaging process this is not possible using the repackaged JAR.
So, for the embedded C++ JVM to work correctly, all the libraries which are available in the repackaged JAR must also be available on the classpath to the C++ embedded JVM. This is achieved by extracting the BOOT-INF/lib from the repackaged JAR using jar -xvf myapp-exec.jar
and moving the BOOT-INF/lib directory to the target classpath location. The JAR files are included in the CLASSPATH definition in the same order as they appear in the repackaged JAR. This works just fine, and this principal is applied to a number of other projects using the exact same C++ application framework with no issues.
The issue here seems to be with the embedded Tomcat server. The problem I have is that the Tomcat server, which works fine when the repackaged JAR is executed, does not initialise correctly when run by the C++ embedded JVM.
Any help gratefully received!
The spring-boot application is launch like so:
spring_application = new SpringApplication(IncidentConfiguration.class);
IncidentSubscribe subscriber = null;
try
{
spring_context = spring_application.run(args);
subscriber = (IncidentSubscribe) spring_context.getBean("incidentSubscribe");
synchronized (subscriber)
{
if (subscriber.start())
{
subscriber.wait();
}
else
{
log.warn("failed to start Web-Service platform!");
}
}
}
catch (InterruptedException ex)
{
log.info("thread interrupted");
}
Failed Run (C++ embedded JVM)
Start embedded Tomcat
Feb 08, 2018 6:06:05 PM org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer initialize
INFO: Tomcat initialized with port(s): 8080 (http)
Feb 08, 2018 6:06:05 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Tomcat
Feb 08, 2018 6:06:05 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/8.5.14
Initialises AbstractProtocol (not done in successful run)
Feb 08, 2018 6:06:12 PM org.springframework.jmx.export.MBeanExporter afterSingletonsInstantiated
INFO: Registering beans for JMX exposure on startup
Feb 08, 2018 6:06:12 PM org.apache.coyote.AbstractProtocol init
INFO: Initializing ProtocolHandler ["http-nio-8080"]
Feb 08, 2018 6:06:13 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-nio-8080"]
Feb 08, 2018 6:06:13 PM org.apache.tomcat.util.net.NioSelectorPool getSharedSelector
INFO: Using a shared selector for servlet write/read
SEVERE: error REPEATS 1000's of times...
Feb 08, 2018 6:06:13 PM org.apache.tomcat.util.net.NioEndpoint$Acceptor run
SEVERE: Socket accept failed
java.nio.channels.NotYetBoundException
at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:237)
at org.apache.tomcat.util.net.NioEndpoint$Acceptor.run(NioEndpoint.java:453)
at java.lang.Thread.run(Thread.java:745)
Something different happens here...
Feb 08, 2018 6:06:13 PM org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer start
INFO: Tomcat started on port(s): -1 (http)
Feb 08, 2018 6:06:14 PM org.springframework.boot.StartupInfoLogger logStarted
INFO: Started application in 23.874 seconds (JVM running for 33.524)
C++ application detects startup failure and shuts down spring-boot
08/02 18:06:15.127[26491][0:1:Thread-2] XXXAdapter.java:144 ***Warning: start() - stop the embeded TOMCAT server
Feb 08, 2018 6:06:15 PM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@545866ff: startup date [Thu Feb 08 18:05:55 GMT 2018]; root of context hierarchy
Feb 08, 2018 6:06:15 PM org.springframework.jmx.export.MBeanExporter destroy
INFO: Unregistering JMX-exposed beans on shutdown
Feb 08, 2018 6:06:15 PM org.apache.coyote.AbstractProtocol pause
INFO: Pausing ProtocolHandler ["http-nio-8080"]
Feb 08, 2018 6:06:15 PM org.apache.tomcat.util.net.AbstractEndpoint unlockAccept
WARNING: Failed to unlock acceptor for [http-nio-8080] because the local address was not available
Feb 08, 2018 6:06:15 PM org.apache.catalina.core.StandardService stopInternal
INFO: Stopping service Tomcat
Feb 08, 2018 6:06:15 PM org.apache.coyote.AbstractProtocol stop
INFO: Stopping ProtocolHandler ["http-nio-8080"]
Feb 08, 2018 6:06:15 PM org.apache.coyote.AbstractProtocol destroy
INFO: Destroying ProtocolHandler ["http-nio-8080"]
Successful Run (repackaged JAR)
Start embedded Tomcat
Feb 08, 2018 6:39:26 PM org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer initialize
INFO: Tomcat initialized with port(s): 8080 (http)
Feb 08, 2018 6:39:26 PM org.apache.catalina.core.StandardService startInternal
INFO: Starting service Tomcat
Feb 08, 2018 6:39:26 PM org.apache.catalina.core.StandardEngine startInternal
INFO: Starting Servlet Engine: Apache Tomcat/8.5.14
Something different happens here...
Feb 08, 2018 6:39:33 PM org.springframework.jmx.export.MBeanExporter afterSingletonsInstantiated
INFO: Registering beans for JMX exposure on startup
Feb 08, 2018 6:39:33 PM org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer start
INFO: Tomcat started on port(s): 8080 (http)
Feb 08, 2018 6:39:33 PM org.springframework.boot.StartupInfoLogger logStarted
INFO: Started XXXApplication in 19.691 seconds (JVM running for 26.538)
Then it closes down...
Feb 08, 2018 6:39:33 PM org.springframework.context.support.AbstractApplicationContext doClose
INFO: Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@423419: startup date [Thu Feb 08 18:39:17 GMT 2018]; root of context hierarchy
Feb 08, 2018 6:39:33 PM org.springframework.jmx.export.MBeanExporter destroy
INFO: Unregistering JMX-exposed beans on shutdown
Feb 08, 2018 6:39:33 PM org.apache.catalina.core.StandardService stopInternal
INFO: Stopping service Tomcat
Example C++ Code as requested
OK, the C++ framework contains many more functions than just this start()
method, so as it is quite complicated I have extracted the relevent bits and stripped out the majority of error checking and resource release code for clarity.
The common JVM structure
struct
{
JavaVM* handle; ///< Handle to Java Virtual Machine (JVM).
jclass adapterClass; ///< JNI jclass for user class.
jobject adapterObject; ///< JNI jobject for instance of user class.
jmethodID adapterStart; ///< JNI method ID of 'start' function for user class.
jclass XXXAdapterClass; ///< JNI jclass for class XXXAdapter.
jmethodID AdapterClassLoader; ///< JNI method ID for static method setClassLoader.
} JVM;
JVM configuration and initialisation
JavaVMInitArgs vm_args;
vm_args.nOptions = count;
vm_args.options = options;
vm_args.version = JNI_VERSION_1_4;
vm_args.ignoreUnrecognized = JNI_FALSE;
// Create the Java Virtual Machine
JNIEnv* jni = NULL;
int success = (JNI_CreateJavaVM((JavaVM**)&JVM.handle, (void**)&jni, &vm_args) != JNI_ERR);
if (!success)
{
SM_Throw(AdapterException, "error creating Java Virtual Machine (JVM)");
}
// locate parent class and class loader for use later
//
JVM.XXXAdapterClass = jni->FindClass( "com/xxx/XXXAdapter" );
JVM.AdapterClassLoader = jni->GetStaticMethodID( JVM.XXXAdapterClass, "setClassLoader", "()V")) )
jclass j_adapterClass = 0;
jobject j_adapterObject = 0;
jmethodID j_adapterInit = 0;
// locate the user specified AdapterClass
//
j_adapterClass = jni()->FindClass(adapter_class_path)) )
// check it is inherited from XXXAdapter class
//
jni->IsAssignableFrom(j_adapterClass, JVM.XXXAdapterClass) )
// locate the default class constructor
//
j_adapterInit = jni->GetMethodID( j_adapterClass, "<init>", "()V");
// create a new adapter object for this service
//
j_adapterObject = jni->NewObject( j_adapterClass, j_adapterInit );
// save service adapter instance as a Java global object
//
JVM.adapterObject = jni->NewGlobalRef(j_adapterObject);
// save user adapter class as a Java global reference
//
JVM.adapterClass = jni->NewGlobalRef(j_adapterClass);
JVM.adapterStart = jni->GetMethodID( j_adapterClass, "start", "()V");
jni->DeleteLocalRef(j_adapterClass);
jni->DeleteLocalRef(j_adapterObject);
jni->ExceptionClear();
and finally, call adapter's start()
function
JNIEnv* jni;
// attach to current Java thread to get handle to JNI environment
//
if (JVM.handle->AttachCurrentThread((void**)&jni, NULL) < 0)
{
SM_Throw(AdapterException, "JAVAAdapter: error during AttachCurrentThread!");
}
// always use the parent class ClassLoader
//
jni->CallStaticVoidMethod(JVM.XXXAdapterClass, JVM.AdapterClassLoader, NULL);
if (jni->ExceptionCheck())
{
jni->ExceptionDescribe();
SM_Throw(WarningException, "JAVAAdapter: error during setClassLoader!" );
}
// *** DO THE CALL ***
//
jni->CallVoidMethod(JVM.adapterObject, JVM.adapterStart);
// and detach from current Java thread
//
JVM.handle->DetachCurrentThread();