Running Linux graphical applications in Docker on Windows with Cygwin/X

Install Babun

Cygwin is a great tool, but not the easiest to install. Babun consists of a pre-configured Cygwin  that does not interfere with existing Cygwin installation.

Download the dist file from http://babun.github.io, unzip it and run the install.bat script. After a few minutes the application will be installed to the %USERPROFILE%\.babun directory. You can use the /target (or /t)  option to install babun to a custom directory.

Install Cygwin/X

Run pact from babun shell (pact is a babun package manager )

pact install xorg-server xinit xhost

Start the X server

Once the installation has completed, open a Cygwin terminal and run XWin :0 -listen tcp -multiwindow. This will start an X server on Windows machine
with the ability to listen to connections from the network (-listen tcp) and display
each application in its own window (-multiwindow), rather than a single window acting
as a virtual screen to display applications on. Once it’s started, you should see an
„X” icon in Windows tray area.

Run graphical application

fr3nd/xeyes  is a good test to run

// don't forget to change WINDOWS_MACHINE_IP_ADDR!
// 'localhost' obviously won't work from within Docker container
docker run -e DISPLAY=$WINDOWS_MACHINE_IP_ADDR:0 --rm fr3nd/xeyes

Or we can build ourselves image with Firefox using the following Dockerfile as a starting point

FROM centos

RUN yum -y update && yum install -y firefox

CMD /usr/bin/firefox

docker build -t firefox . it and run the container with

export DISPLAY=$WINDOWS_MACHINE_IP_ADD:0
docker run -ti --rm -e DISPLAY=$DISPLAY firefox

If all goes well you should see Firefox running from within a Docker container.

Troubleshooting

If you have issues with authorization you may want to try running the insecure xhost + command to permit access from all machines. See xhost(1) Linux man page.

Alternatives

There are a few different options to run GUI applications inside a Docker container like using SSH with X11 forwarding or VNC.

Reklama

Podstawy Akka – izolacja aktorów

Aktorzy w Akka mogą się komunikować tylko przez asynchroniczne przesyłanie komunikatów. Do wysłania komunikatu potrzebny jest uchwyt na aktora (lub selecja) który może być uzyskany w wyniku

  • rodzicielstwa (parenthood) – aktor A tworzy B więc posiada na niego referencję
  • obdarowania (endowment) – kiedy A tworzy B może mu przekazać cześć lub wszystkie uchwyty które sam posiada
  • przedstawienia (introduction) –  jeśli aktor A posiada uchwyty na B i C może wysłać do C komunikat zawierający uchwyt do B. B może wówczas zachować ten uchwyt do dalszego użycia
  • selekcji – w praktyce selekcja jest bardzo podobna do uchwytu. Aktor może wyszukać innych aktorów po nazwach stosując m.in wyrażenia wieloznaczne (context.actorSelection(„/user/*”)) i ścieżki względne (context.actorSelection(„../*”))

 

http://doc.akka.io/docs/akka/current/general/addressing.html

Scala partial function literal

In Scala there’s no literal for partial function types. You have to give the exact signature of the PartialFunction

val pf: PartialFunction[Double, Double] = { case x if x >= 0 => Math.sqrt(x) }
view raw gistfile1.scala hosted with ❤ by GitHub

Though I personally prefer to use type ascription bacause you can define and lift in the same place if needed.

val pf = { case x if x >= 0 => Math.sqrt(x) } : PartialFunction[Double, Double]
view raw gistfile1.scala hosted with ❤ by GitHub

It’s unwieldy, but I’ve learned to live with it.

But lately someone (can’t remember who – sorry!) told me that you don’t need a literal because you can write your own type alias and use it with infix operator concise syntax.

type :=>[A, B] = PartialFunction[A, B]
val pf: Double :=> Double = { case x if x >= 0 => Math.sqrt(x) }
view raw gistfile1.scala hosted with ❤ by GitHub

Changing the directory for Vagrant boxes

The successfully downloaded Vagrant boxes are located at ~/.vagrant.d/boxes on Mac/Linux System and %USERPROFILE%/.vagrant.d/boxes on Windows. This can be changed by setting an environment variable named VAGRANT_HOME to specify the location of .vagrant.d, as in VAGRANT_HOME=F:\.vagrant.d

# Windows
# current terminal session
set VAGRANT_HOME=F:\.vagrant.d
# current user environment
setx VAGRANT_HOME F:\.vagrant.d
# system wide environment
setx VAGRANT_HOME F:\.vagrant.d /M

VAGRANT_HOME is documented here along with other interesting options.

Docker Clean Up!

Over time docker eat up the disk space of the host. Here’s a script to keep it under control.

#!/bin/bash
# Author: Jarek Przygódzki <jarek.przygodzki@gmail.com>
#
# Docker 1.9+
#
# In Docker 1.9, there is the ability to manage volumes better
# with docker volume (see https://github.com/docker/docker/pull/14242)
#
# Delete all stopped containers
echo "Delete all stopped containers"
docker ps -q -f status=exited | xargs --no-run-if-empty docker rm
# Delete all dead containers
echo "Delete all dead containers"
docker ps -q -f status=dead | xargs --no-run-if-empty docker rm
# Delete all dangling (unused) images
echo "Delete all dangling (unused) images"
docker images -q -f dangling=true | xargs --no-run-if-empty docker rmi
# Delete all dangling (orphaned) volumes
echo "Delete all dangling (orphaned) volumes"
docker volume ls -qf dangling=true | xargs --no-run-if-empty docker volume rm

Use with care.

curl -fsSL https://git.io/voKIi | sh

Slick i otwarte sterowniki do komercyjnych baz

W wersji 2.0 Slicka firma Typesafe usunęła sterowniki (profile) do komercyjnych baz (Oracle, IBM DB2 i Microsoft SQL Server) i zaczęła pobierać za nie opłaty. Był to ruch w pełni legalny z punktu widzenia licencji BSD and Apache OSS ale  jednocześnie bardzo irytujący – nie tyle ze względu na koszty ile biurokratyczne wyzwania związane z uzyskaniem  zgody na zakup jakiejkolwiek dodatkowych licencji w typowym projekcie . Ruch ten spotkał się ostrą krytyką i zaowocował powstaniem sterowników wspieranych przez społeczność i dostępnych na licencji Copyleft (LGPL) – FreeSlick.

Monitoring WildFly Using BTrace

Today I was hoping to use BTrace to track a rather anoying bug in web application running inside WildFly application server. I’ve used BTrace some times in the past and my experience was nothing but positive. Until today, that is.

Probe deployment caused  a myriad of exceptions inside application server coming from instrumented classes  and all caused by missing btrace runtime.   The classloader separation of the AS getting in my way! I know a thing or two about classloading in WildFly, so I decided to add btrace  to system packages(just like Byteman)

jboss.modules.system.pkgs=org.jboss.byteman,com.sun.btrace

To my suprise, it wasn’t the end of the story because now my btrace script was unavailable

java.lang.ClassNotFoundException: TracingScript$1 from [Module "deployment.atrem.incubator.e5.ml-web.war:main" from Service Module Loader]
	at org.jboss.modules.ModuleClassLoader.findClass(ModuleClassLoader.java:198)
	at org.jboss.modules.ConcurrentClassLoader.performLoadClassUnchecked(ConcurrentClassLoader.java:363)
	at org.jboss.modules.ConcurrentClassLoader.performLoadClass(ConcurrentClassLoader.java:351)
	at org.jboss.modules.ConcurrentClassLoader.loadClass(ConcurrentClassLoader.java:93)
	... 120 more

My solution was to place tracing script in non-root package btrace.scripts

package btrace.scripts;

import com.sun.btrace.*;
import com.sun.btrace.annotations.*;
import static com.sun.btrace.BTraceUtils.*;

@BTrace
public class TracingScript {
//
}

and add the package itself to system packages

jboss.modules.system.pkgs=org.jboss.byteman,com.sun.btrace,btrace.scripts

which finally made it work. Now, back to the problem at hand.

JMS bridge between WildFly instances

I was recently trying to set up JMS bridge between two standalone WildFly instances, each running on different machine. The whole experience turned out to be less straightforward than I had anticipated and I decided I should share the solution here to hopefully help others.

The solution

It turns out that to configure JMS bridge for remote servers credentials need to be specified twice. The username and password must be added to the context to allow JNDI registry lookup and then again in destination configuration. The user referred in <user> tag must be registered in ApplicationRealm with permission to consume/send messages (depending on whether source or target is remote endpoint)

<jms-bridge name="jms-bridge-name">
<source>
<connection-factory name="jms/RemoteConnectionFactory"/>
<destination name="jms/queue/sourceQ"/>
<!-- jmsuser with the same password is registered in ApplicationRealm with permission to consume/send messages -->
<user>jmsuser</user>
<password>pass</password>
<context>
<!-- The username and password must be added to the context to allow remote registry lookup -->
<property key="java.naming.factory.initial" value="org.jboss.naming.remote.client.InitialContextFactory"/>
<property key="java.naming.provider.url" value="http-remoting://host.domain:8080"/>
<property key="java.naming.security.principal" value="jmsuser"/>
<property key="java.naming.security.credentials" value="pass"/>
</context>
</source>
<target>
<connection-factory name="ConnectionFactory"/>
<destination name="jms/queue/targetQ"/>
</target>
<quality-of-service>ONCE_AND_ONLY_ONCE</quality-of-service>
<failure-retry-interval>10000</failure-retry-interval>
<max-retries>10</max-retries>
<max-batch-size>500</max-batch-size>
<max-batch-time>50</max-batch-time>
<add-messageID-in-header>true</add-messageID-in-header>
</jms-bridge>

The problem

Before connection to JMS queue can be established , JMS bridge must first  lookup remote objects  (queues and connection factories) by their JDNI names. When both bridge and remote broker are running on the same machine no authentication is required, but for truly remote  lookup to succeed valid credentials are needed. It’s worth mentioning that the user used for JDNI lookup can be different from the one used for creating the JMS connection and doesn’t need permission to modify queue.

Failure to provide credentials in context definition will result in rather cryptic error

javax.security.sasl.SaslException: Authentication failed: all available authentication mechanisms failed:
JBOSS-LOCAL-USER: javax.security.sasl.SaslException: Failed to read server challenge [Caused by java.io.FileNotFoundException: /Users/Jarek/dev/wildfly-8.2.0.Final/standalone/tmp/auth/local2330515126078615287.challenge (System nie może odnaleźć określonej ścieżki)]
DIGEST-MD5: javax.security.sasl.SaslException: DIGEST-MD5: Cannot perform callback to acquire realm, authentication ID or password [Caused by javax.security.auth.callback.UnsupportedCallbackException]

When user name or password are present but invalid the error message is a little more useful

javax.security.sasl.SaslException: Authentication failed: all available authentication mechanisms failed:
JBOSS-LOCAL-USER: javax.security.sasl.SaslException: Failed to read server challenge [Caused by java.io.FileNotFoundException: /Users/Jarek/dev/wildfly-8.2.0.Final/standalone\tmp\auth\local6004500283609774891.challenge (System nie może odnaleźć określonej ścieżki)]
DIGEST-MD5: Server rejected authentication

Only components that are registered within the java:jboss/exported/ namespace are remote accessible via the naming service.

Recipe

In WildFly, JMS bridge between local and remote destinations  jms/queue/sourceQ ⇒ jms/queue/targetQ with ONCE_AND_ONLY_ONCE QoS can be created with following CLI command

/subsystem=messaging/jms-bridge=jms-bridge-name/:add(source-destination="jms/queue/sourceQ", \ 
    source-connection-factory="ConnectionFactory", \
    target-destination="jms/queue/targetQ", \
    target-connection-factory="jms/RemoteConnectionFactory", \
    target-user="jmsuser", \
    target-password="pass", \
    target-context=[\
        "java.naming.factory.initial"="org.jboss.naming.remote.client.InitialContextFactory", \
        "java.naming.provider.url"="http-remoting://localhost:8080", \
        "java.naming.security.principal"="jmsuser", \
        "java.naming.security.credentials"="pass"], \
    quality-of-service=ONCE_AND_ONLY_ONCE, \
    failure-retry-interval=10000, \
    max-retries=10, \
    max-batch-size=500, \
    max-batch-time=50, \
    add-messageID-in-header=true)

Setting up jTDS with Wildfly

Setting up jTDS JDBC driver and creating datasource in Wildfly is pretty easy with CLI. It involes installing JDBC driver

# Add jTDS JDBC driver as a module
module add --name=net.sourceforge.jtds --resources=/path/to/jtds.jar --dependencies=javax.api,javax.transaction.api
# Add driver configuration
if (outcome != success) of /subsystem=datasources/jdbc-driver=jTDS/:read-resource
    /subsystem=datasources/jdbc-driver=jTDS/:add(driver-name=jTDS, driver-module-name=net.sourceforge.jtds, driver-class-name=net.sourceforge.jtds.jdbc.Driver, driver-xa-datasource-class-name=net.sourceforge.jtds.jdbcx.JtdsDataSource)
end-if

add adding datasource(s)

data-source add \
--name=[DsName] \
--driver-name=jTDS \
--connection-url=jdbc:jtds:sqlserver://[DbHost]:1433/[DbName]\
--share-prepared-statements=true \
--jndi-name=java:/jdbc/[Name]\
--user-name=[DbUser] \
--password=[DbPass] \
--min-pool-size=1 \
--max-pool-size=50 \
--background-validation=true \
--background-validation-millis=600000 \
--valid-connection-checker-class-name=org.jboss.jca.adapters.jdbc.extensions.mssql.MSSQLValidConnectionChecker

Jak podłączyć się JConsole do serwera WildFly

Ogólnie
%JBOSS_HOME%\bin\jconsole service:jmx:http-remoting-jmx://[HOST]:[MANAGEMENT_PORT]
W przypadku połączenia do serwera uruchomionego lokalnie nie jest najczęściej wymagana autoryzacja
%JBOSS_HOME%\bin\jconsole service:jmx:http-remoting-jmx://127.0.0.1:9990