Java GUI can easily be used on a headless server via Xvnc or Xvfb and x11vnc. Read on.
By default there is no graphical display device support on VPSes. We inform JVM about it with -Djava.awt.headless=true
in $JAVA_OPTS
environment variable or in Java command line. Generating text-only output from a Java server or application is faster and uses less resources so it is preferred in general. If your code just requires graphical buffer then usual approach is to install Xvfb
server (frame buffer emulator) then export DISPLAY=:0
and append -Djava.awt.headless=false
to $JAVA_OPTS
.
You may later run x11vnc
and connect it with Xvfb
this way allowing for remote connection to the display with VNC protocol. You may also choose to replace Xvfb/x11vnc tandem with Xvnc (frame buffer + VNC server in one). We will present the Xvnc method below.
Let's have a simple GUI java code built (-Djava.awt.headless=false
is already included in $JAVA_OPTS
environment variable - you can also provide the switch in command line if you wish). Put the below dialog code in GUITest.java
file, compile and execute it.
cat >GUITest.java<<EOF
import javax.swing.JOptionPane;
public class GUITest {
public static void main(String[] args) {
JOptionPane.showMessageDialog(null, "Click OK to close the application");
System.exit(0);
}
}
EOF
javac GUITest.java
java GUITest
Exception in thread "main" java.awt.HeadlessException:
No X11 DISPLAY variable was set, but this program performed an operation which requires it.
at java.awt.GraphicsEnvironment.checkHeadless(GraphicsEnvironment.java:159)
at java.awt.Window.(Window.java:431)
at java.awt.Frame.(Frame.java:403)
at java.awt.Frame.(Frame.java:368)
at javax.swing.JFrame.(JFrame.java:158)
at GUITest.(GUITest.java:6)
at GUITest.main(GUITest.java:12)
We are on a VPS so can arbitrarily set our display number. Let it be 0. It is denoted as displaynumber:screennumber. We will omit displaynumber leaving it with default value of 0 and by display we will refer to value of DISPLAY variable.
export DISPLAY=:0
java GUITest
Exception in thread "main" java.lang.UnsatisfiedLinkError: /opt/jdk1.6.0_33/jre/lib/amd64/xawt/libmawt.so: libXext.so.6: cannot open shared object file: No such file or directory
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1807)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1703)
at java.lang.Runtime.load0(Runtime.java:770)
at java.lang.System.load(System.java:1003)
at java.lang.ClassLoader$NativeLibrary.load(Native Method)
at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1807)
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1724)
at java.lang.Runtime.loadLibrary0(Runtime.java:823)
at java.lang.System.loadLibrary(System.java:1028)
at sun.security.action.LoadLibraryAction.run(LoadLibraryAction.java:50)
at java.security.AccessController.doPrivileged(Native Method)
at sun.awt.NativeLibLoader.loadLibraries(NativeLibLoader.java:38)
at sun.awt.DebugHelper.(DebugHelper.java:29)
at java.awt.Component.(Component.java:566)
Could not find the main class: GUITest. Program will exit.
The problem here is missing libXext
library (and a few more libraries) that we install with dependencies using yum install libXext libXtst libXi twm
. TWM is a lightweight window manager that will provide frame and control buttons to our window. When running the program now we will see:
java GUITest
Exception in thread "main" java.lang.InternalError: Can't connect to X11 window server using ':0' as the value of the DISPLAY variable.
at sun.awt.X11GraphicsEnvironment.initDisplay(Native Method)
at sun.awt.X11GraphicsEnvironment.access$100(X11GraphicsEnvironment.java:52)
at sun.awt.X11GraphicsEnvironment$1.run(X11GraphicsEnvironment.java:155)
at java.security.AccessController.doPrivileged(Native Method)
at sun.awt.X11GraphicsEnvironment.(X11GraphicsEnvironment.java:131)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:169)
at java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:68)
at java.awt.Window.init(Window.java:379)
at java.awt.Window.(Window.java:432)
at java.awt.Frame.(Frame.java:403)
at java.awt.Frame.(Frame.java:368)
at javax.swing.JFrame.(JFrame.java:158)
at GUITest.(GUITest.java:6)
at GUITest.main(GUITest.java:12)
We instructed our JVM to connect to display 0 but there is no X server servicing the display. Let's install Xvnc. We STRONGLY recommend to have your Java running as regular (non-root) user. In this example we use root account for simplicity.
yum install vnc-server
echo -e "VNCSERVERS=\"1:root\"\nVNCSERVERARGS[1]=\"-geometry 800x600\"" >> /etc/sysconfig/vncservers
vncpasswd
/etc/init.d/vncserver start
Starting VNC server: 0:root
xauth: creating new authority file /root/.Xauthority
New 'guitest:0 (root)' desktop is guitest:0
Creating default startup script /root/.vnc/xstartup
Starting applications specified in /root/.vnc/xstartup
Log file is /root/.vnc/guitest:0.log
We can now run our program and it should connect to the display without issues. Connect with a VNC client to your VPS to see the created dialog for example
[user@remotehost ~]#vncviewer 10.10.10.10:0
You should see below dialog and the application will exit afer your click the button.
In case you want to try Xvfb + x11vnc method on Centos 5 the quick guide follows. You can download and install x11vnc for CentOS 5 from https://pkgs.org/download/x11vnc/.
yum install Xvfb libXdamage libXfixes libXinerama libXrandr avahi libjpeg xorg-x11-utils xorg-x11-server-Xorg libXext libXtst libXi twm
adduser xvfb; chsh -s /bin/false xvfb; touch /var/log/Xvfb.log; chown xvfb /var/log/Xvfb.log
export DISPLAY=:0
/usr/bin/Xvfb :0 -screen 0 800x600x24 >> /tmp/Xvfb.out 2>&1 &
twm >> /tmp/Xvfb.out 2>&1 &
java GUITest &
rpm -ivh x11vnc-0.9.13-1.el5.rf.x86_64.rpm
x11vnc --nopw
Now connect with a VNC viewer just like in the previous example.