Saturday, September 30. 2017
Clustered stateful EJB in Wildfly called from standalone java client
Long time ago I posted an article about configuring an EJB in a clustered glassfish. During this summer I had to deal with some issues about this subject but using Wildfly. I discovered that, in this container, there are several possibilities of doing a remote call to an EJB, but only one of them is the good one. So this entry is going to setup this good configuration step by step. This same option is a little different when the client is another Wildfly server (a wildfly frontend communicates with another Wildfly backend in which the EJBs are running) than when the client is a standalone java app or another container. Today's entry covers the last case, using a non-wildfly client.
Installing the Wildfly cluster
First a clustered wildfly backend needs to be installed. Both instances are going to be running in the same machine, using a port offset in the one of them. The steps are the following.
Install the software, create an admin user for the management console and start the domain.
wget http://download.jboss.org/wildfly/10.1.0.Final/wildfly-10.1.0.Final.zip unzip wildfly-10.1.0.Final.zip cd wildfly-10.1.0.Final/bin ./add-user.sh -u admin -p adminadmin ./domain.sh
By default wildfly generates some configuration (groups and servers) that I usually remove. From now on the jboss-cli.sh command is used to configure the domain.
./jboss-cli.sh --connect /host=master/server-config=server-one:stop(blocking=true) /host=master/server-config=server-two:stop(blocking=true) /host=master/server-config=server-three:stop(blocking=true) /host=master/server-config=server-one:remove() /host=master/server-config=server-two:remove() /host=master/server-config=server-three:remove() /server-group=main-server-group:remove() /server-group=other-server-group:remove()
With an empty domain two servers are created in the ha profile. In order to have a cluster you have to use at least this profile (full-ha profile would be also ok). Please take into account that the second server will use an offset of 10000. Besides a property jboss.node.name is set to each instance with the name (this property will be used later in the EJB to know which instance is executing it).
/server-group=ha-server-group:add(profile=ha,socket-binding-group=ha-sockets) /host=master/server-config=ejb-server-1:add(group=ha-server-group) /host=master/server-config=ejb-server-2:add(group=ha-server-group,socket-binding-port-offset=10000) /host=master/server-config=ejb-server-1/system-property=jboss.node.name:add(value=ejb-server-1,boot-time=true) /host=master/server-config=ejb-server-2/system-property=jboss.node.name:add(value=ejb-server-2,boot-time=true) /host=master/server-config=ejb-server-1:start(blocking=true) /host=master/server-config=ejb-server-2:start(blocking=true)
The cluster will use default configuration. Wildfly use jgroups for cluster communication and this library can be configured in different ways, by default it uses udp and multicast for cluster communication and discovery. If you prefer tcp and unicast further configuration is needed.
Finally in order to use the EJB remotely an application user is created (this username and password will be configured in the application).
./add-user.sh -a -u ejbuser -p ejbuser
At this point we have two wildfly instances in cluster (ha profile) running in the same machine.
Developing the EJB server
So now is the time to implement our little stateful EJB. The interface is very simple.
public interface RemoteCounter {
void increment();
void decrement();
int getCount();
String getJbossNode();
}
The maintained state is just a counter and the last method will be used to return the jboss.node.name property (which server is attending the call). The implementation is straight forward.
@Stateful
@Remote(RemoteCounter.class)
public class CounterBean implements RemoteCounter {
private int count = 0;
@Override
public void increment() {
this.count++;
}
@Override
public void decrement() {
this.count--;
}
@Override
public int getCount() {
return this.count;
}
@Override
public String getJbossNode() {
return System.getProperty("jboss.node.name");
}
}
Developing the client
This part is the interesting one, in a standalone java application (or using a non-wildfly app container) the configuration for the EJB is done using a jboss-ejb-client.properties which should be available in the classpath. This file defines the remote connections where the EJBs are going to be located. You can define more than one endpoint and the implementation will try to connect to them one by one. But, for a cluster setup, there is another vital part: the cluster configuration. When an EJB client connects to a server that is configured in cluster, it receives the cluster formation data (when a member in the cluster shuts down or when a member joins to the cluster). This way the client can provide clustering features (high availability, load balancing,...). The configuration that the client uses to connect to the cluster instances is the one that is defined in the cluster part of this file (I know the configuration is repeated multiple times but it was thought like this).
In summary, for my little setup the following jboss-ejb-client.properties is used:
remote.connections=ejbserver1,ejbserver2
remote.connection.ejbserver1.host=localhost
remote.connection.ejbserver1.port=8080
remote.connection.ejbserver1.protocol=http-remoting
remote.connection.ejbserver1.username=ejbuser
remote.connection.ejbserver1.password=ejbuser
remote.connection.ejbserver2.host=localhost
remote.connection.ejbserver2.port=18080
remote.connection.ejbserver2.protocol=http-remoting
remote.connection.ejbserver2.username=ejbuser
remote.connection.ejbserver2.password=ejbuser
remote.clusters=ejb
remote.cluster.ejb.connect.options.org.xnio.Options.SASL_POLICY_NOANONYMOUS=true
remote.cluster.ejb.protocol=http-remoting
remote.cluster.ejb.username=ejbuser
remote.cluster.ejb.password=ejbuser
There are defined two connections (for each member of the cluster) and the configuration for the cluster (the connection data for each member of the cluster when members are received by the client). Think that you can omit one connection but then, if the only defined server is down when connecting, the client will fail although the second server is up and running (the cluster information is only used when the connection starts and the cluster formation information is received). Then the client is developed as shown in the jboss documentation.
InitialContext context = null;
try {
Properties jndiProperties = new Properties();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
context = new InitialContext(jndiProperties);
// lookup name is a bit tricky, check documentation
String lookupName = "ejb:/stateful-ejb-wildfly/CounterBean!es.rickyepoderi.samples.ejb.RemoteCounter?stateful";
RemoteCounter statefulRemoteCounter = (RemoteCounter) context.lookup(lookupName);
statefulRemoteCounter.increment();
// Use the stateful EJB
// ...
} finally {
if (context != null) {
try {
context.close();
} catch (Exception e) {
}
}
}
Demo
Here you have a simple video that shows the demo EJB application in action. The server ejb-server-1 is down at the beginning so, when the client is started, it fails to connect to the first connection but establishes it with the second server. After some increments in the second server the first one is started again. The client (as the server backend is clustered) receives that a new member is active. This way when the second server is shut down the stateful EJB can continue using the new discovered first server.
And that is all. Remember that there are more options to configure a remote EJB client but this is the good one. If, for whatever reason, you cannot use a fixed properties file there is a programmatic way of specifying the same configuration, although I have never used it. You can download the sample application I used for this entry from here.
Best regards!
Thursday, September 21. 2017
Adding a custom CA to your android phone
Another simple post this time. I updated my phone again with LineageOS (I was waiting if any release other than nightly was to be available, but the blueborne exploit convinced me to move now) and, again, I spent some time trying to install a custom certificate in the phone. The procedure is explained in this article in detail and it works like a charm for me.
To install your CA certificate you need to obtain a hash of the certificate because android expects the file to be called using this hash (this point was crucial and I had no idea about it before). So first you obtain your PEM and get the hash with the following command:
openssl x509 -inform PEM -subject_hash_old -in calocal.crt | head -1 xxxxxxxx
Once you have the certificate and the hash you just need to install it with the following name /system/etc/security/cacerts/xxxxxxxx.0 and the same permissions than the other CAs. Remember that the /system file system is read-only by default and you need su to do the complete process. So you have to go to Settings → Developer Options → Root Access and include ADB to it.
$ adb shell daemon not running. starting it now on port 5037 daemon started successfully $ su # mount -o remount,rw /system # cp /sdcard/Downloads/calocal.crt /system/etc/security/cacerts/xxxxxxxx.0 # chmod 644 /system/etc/security/cacerts/xxxxxxxx.0 # mount -o remount,ro /system
And reboot your phone. You can check that your local CA now should be listed in Settings → Security → Trusted Credentials in the System tab.
Following the procedure all my synchronizations started to work again against my personal nextcloud and runalyze servers. It's incredible that such an old phone is still maintained by lineage and now running android 7.1.2 with the September security patches from google. I have to say that my requirements for the phone are very slight.
Good job lineage team!
Sunday, September 10. 2017
Custom mode lines in Wayland
Because of my new work now I only use debian testing in my desktop box. I have a little gigabyte next to my TV and, if you remember, my nasty samsung model is always boycotting me (using an HDMI connector the TV uses a mode in which a little zone at the four edges is missing, other samsung models have a screen fit option that avoids this issue, but in mine that option is missing; if common VGA connection is used then the TV does not send the EDIDs to the computer). I am not going to buy another samsung TV in my whole life, that is for sure. But after upgrading the box I discovered that (again) the resolution was 1024x768 and not to the correct 1360x768 that is supported in VGA, that mode line was configured in the xorg.conf file and now, something had changed, and it did not work. To my surprise debian testing has moved to wayland and now the X11 configuration is not used (if I logged in using X11 it worked again with the proper resolution).
I do not know if I am going to use wayland or X11 but at least I wanted to know how to configure a custom mode line in wayland to have both options available, and I finally discovered it in this post answer. Now you have to add the mode line in EDID format and configure the kernel to boot using that EDID. The steps are quite easy:
Akatrevorjay has created an easy project to change any X11 mode line in an EDID file. So first clone the project.
git clone https://github.com/akatrevorjay/edid-generator.git cd edid-generator
Install some required packages.
sudo apt-get install zsh edid-decode automake dos2unix
The project has some standard mode lines already present but I had to add my custom one. This creates a 1360x768.S definition file for the mode line.
./modeline2edid - <<< 'Modeline "1360x768" 84.75 1360 1432 1568 1776 768 771 781 798 -hsync +vsync'
Compile and create the binary EDID files.
make
Copy the ones you are interested in to the /lib/firmware/edid directory.
sudo cp 1024x768.bin 1360x768.bin /lib/firmware/edid/
Finally add the EDID you want to configure in the kernel at grub level. The option drm_kms_helper.edid_firmware=edid/<resolution>.bin should be added to the GRUB_CMDLINE_LINUX variable in the /etc/default/grub file (In debian this option is empty by default).
GRUB_CMDLINE_LINUX="drm_kms_helper.edid_firmware=edid/1360x768.bin"
You can first try adding manually the option editing the boot line in grub (in the grub menu you can click e to edit the line and start with the option manually added, you also need to do this if you make any mistake and the kernel does not boot).
Update the grub.
sudo update-grub
And that is all. When you restart the box, it uses the new resolution in the complete boot process. I do not know if I will continue with wayland or I switch back to X11 (think that this box is mainly used to browse internet, watch youtube videos, series and films, listen to music and so on and so forth), for the moment I have not seen any issue but you never know. As always, the entry is mainly for me, next time I will have the information here, easy access.
Regards!
Comments