tag:blogger.com,1999:blog-16257765335666260662024-03-14T00:26:47.170+01:00Playing JEE on the PiJava JEE technologies are about distributed computing across the enterprise. The Raspberry Pi is a low cost ARM based computer. It runs Linux and works in a network. A couple of Pis in a network are a very interesting environment for playing around with the latest JEE stuff. That's what this blog is about: running JEE technologies on small devices and making distributed computing tangible.
Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.comBlogger14125tag:blogger.com,1999:blog-1625776533566626066.post-47100460238636739772018-10-15T15:11:00.000+02:002018-11-03T12:59:13.392+01:00Local Kubernetes Cluster on CentOS7 with Vagrant and VirtualBox on your Windows Laptop Part II<h2>
Part II: Explaining the Vagrantfile for provisioning the Cluster Nodes</h2>
<div>
<a href="https://jeeonthepi.blogspot.com/2018/09/local-kubernetes-cluster-on-centos7.html" target="_blank">Part I</a> of this blog was about setting up our Kubernetes cluster "out of the box", i.e. without customizing the Vagrantfiles.<br />
This part will explain the Vagrantfiles in detail. Every single step will be explained, so that you are ready for part III.<br />
Part III will then explain how to customize certain parts of the Vagrantfile, namely the hostnames and IP addresses of your nodes and how to add additional nodes to the cluster.<br />
<h2>
Updates</h2>
<h3>
03.11.2018</h3>
ToDo: Changes to the Vagrantfile have to be included here.</div>
<div>
<h2>
Vagrantfiles for Master and Worker </h2>
<div>
As you have seen in Part I, every node is started from its respective Vagrantfile located in its own directory.<br />
This means that we have two types of Vagantfiles: a Vagrantfile to provision the master node and Vagrantfiles to provision each worker node respectively.<br />
<br />
Vagrant offers different provisioners like Ansible or Puppet. Here I'm using the Bash provisioner, which means that you may also run the commands manually in a Bash shell instead of using Vagrant.<br />
<h3>
Provisioning the Master Node</h3>
</div>
</div>
<div>
Let's take a look at the Vagrantfile of the master node, located in the cl2master directory.</div>
<div>
The first section has little to do with Kubernetes and is more or less self-explanatory.</div>
<div>
I will cover this quickly:</div>
<div>
<br /></div>
<pre class="brush: bash"> -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box = "geerlingguy/centos7"
# Der Box einen Namen geben
# ...und Memory und CPU für Kubernetes hochsetzen
# Assign a name to this box
# ...define momory and number of CPUs
config.vm.provider "virtualbox" do |v|
v.name = "cl2master"
v.memory = 2048
v.cpus = 2
end
# Hostname definieren
# Set Hostname
config.vm.hostname = "cl2master"
# Die korrekte Zeitone einstellen:
# Set local timezone
if Vagrant.has_plugin?("vagrant-timezone")
config.timezone.value = "Europe/Berlin"
end
# Create a private network, which allows host-only access to the machine
# using a specific IP.
config.vm.network :private_network, ip: "192.168.2.10", auto_config: false
# Enable provisioning with a shell script. Additional provisioners such as
# Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
# documentation for more information about their specific syntax and use.
</pre>
<div>
We define "<a href="https://app.vagrantup.com/geerlingguy/boxes/centos7" target="_blank">geerlingguy/centos7</a>" as our Vagrant Basebox. Everything we do from here, will be based on this image. I'm using this image since a couple of years, every time I need virtual machine based on CentOS 7. It is very popular within the vagrant community. Find everything you need to know about this box <a href="https://app.vagrantup.com/geerlingguy/boxes/centos7" target="_blank">here</a>.
</div>
<br />
The next section gives a name to the virtual machine and defines memory and cpu requirements.<br />
<br />
Then a host name is given to the machine and its time zone is set to where I live.<br />
One remark here: this requires a the vagrant timezone plugin to be installed. If you don't have this installed and do not wish to, delete this section. Else set it to time zone of your choosing.<br />
<br />
Next we configure a host-only network to be set up. The network adapter will be reconfigured in a later step to make this absolutely fail-safe.<br />
<br />
Now to provisioning the machine. Its done using Vagrant's shell provisioner in inline mode. Everything between "<span style="background-color: white; font-family: "courier new";"><<-SHELL" </span>and "<span style="background-color: white; font-family: "courier new";">SHELL</span>" is done to provision the machine as Kubernetes master node.<br />
First thing to is bringen the machine up to date (step 0, if you like).<br />
Step 1 then brings Docker into your machine:<br />
<ul>
<li>add the docker repository to yum</li>
<li>install docker engine</li>
<li>create a docker group and add user "vagrant" to it</li>
<li>start the docker service</li>
</ul>
<div>
From now on, you can update your docker installation via yum.</div>
<pre class="brush: bash">echo "Box auf den neuesten Stand bringen..."
echo "bringing box up-to-date..."
sudo yum update -y
echo "Schritt 1: Docker installieren und einrichten"
echo "Step 1: install and set up Docker"
echo "============================================="
echo "...Docker Repo als yum Repo hinzufügen"
sudo tee /etc/yum.repos.d/docker.repo <<-'EOF'
[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/centos/7/
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg
EOF
echo "...Docker installieren"
sudo yum install docker-engine -y
echo "Docker group anlegen und vagrant-user zufügen..."
echo "create Docker group and add vagrant-user..."
sudo groupadd docker
sudo usermod -aG docker vagrant
sudo chkconfig docker on
sudo systemctl start docker
echo "Schritt 1 erledigt"
echo "Step 1 done"
echo "------------------"
</pre>
<br />
<div>
<div>
Step 2 adds the Kubernetes repository to yum.<br />
Note that there base-url is architecture specific.</div>
echo "Schritt 2: Kubernetes Repo als yum Repo hinzufügen"</div>
<pre class="brush: bash">echo "Step 2: Add Kubernetes Repo to yum Repo definitions"
echo "=================================================="
sudo tee /etc/yum.repos.d/kubernetes.repo <<-'EOF'
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
echo "Schritt 2 erledigt"
echo "Step 2 done"
echo "------------------"
</pre>
<br />
<div>
Step 3 is done to prevent iptables routing errors, specifically done on CentOS systems.<br />
Kernel Parametes are modified by writing them to a config file for sysctl and then explicitely loading them:<br />
echo "Schritt 3: iptables routing errors unter CentOS verhindern"</div>
<pre class="brush: bash">echo "prevent iptables routing errors reported under CentOS"
echo "=========================================================="
sudo tee /etc/sysctl.d/k8s.conf <<-EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sudo sysctl --system
echo "Schritt 3 erledigt"
echo "Step 3 done"
echo "------------------"
</pre>
<br />
Step 4 is to disable SeLinux.
<br />
At the time of this writing, Kubernetes and SeLinux do not work well together. Therefore SeLinux gets disabled temporarily and permanently:<br />
<pre class="brush: bash">echo "Schritt 4: SeLinux abschalten, temporär and permanent"
echo "Step 4: switch SeLinux off temporarily and permanently"
echo "====================================================="
sudo tee -a /etc/sysconfig/selinux <<-EOF
SELINUX=disabled
SELINUXTYPE=targeted
EOF
# auch temporär abschalten:
sudo setenforce 0
echo "Schritt 4 erledigt"
echo "Step 4 done"
echo "------------------"
</pre>
<br />
Step 5 is done to disable CentOS swap, again temporarily and permanently. Kubernetes will not install as long as swap is on.<br />
We do this by removing the swap entry from /etc/fstab:<br />
<pre class="brush: bash">echo "Schritt 5: CentoOS Swap abschalten, temporär und permanent"
echo "Step 5: turn CentOS off temporarily and permanently"
sudo swapoff -a
echo "swap permanent ausschalten:"
echo "swap-Zeile rauswerfen in temp-Datei und temp dann zurückkopieren..."
sudo grep -v "swap" /etc/fstab > temp
sudo mv temp /etc/fstab
echo "swap permanent ausgeschaltet!"
echo "Schritt 5 erledigt"
echo "Step 5 done"
echo "------------------"
</pre>
<br />
Step 6 needed to be done on my system, so I included it. Maybe it is not necessary on yours.<br />
The configuration for the cgroup driver in Kubernetes has to match the one configured in docker.<br />
Kubernetes comes with "systemd", so we set the docker configuration accordingly (it was "cgroupfs" on my installation):<br />
<pre class="brush: bash">echo "Schritt 6: docker cgroup driver auf systemd ändern"
echo "Step 6: change docker cgroup driver to systemd"
echo "=================================================="
sudo tee /etc/docker/daemon.json >>-EOF
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
sudo systemctl restart docker
echo "Schritt 6 erledigt"
echo "Step 6 done"
echo "------------------"
</pre>
<br />
Step 7 is done more or less out of a habit. I'm adding the cluster nodes to come into the /etc/hosts file.<br />
<pre class="brush: bash">echo "Schritt 7: /etc/hosts anpassen (das muss man pro Node machen!)"
echo "Step 7: make all other nodes known via /etc/hosts"
echo "--------------------------------------------------------------"
sudo tee -a /etc/hosts >>-EOF
192.168.2.11 cl2node1
192.168.2.12 cl2node2
EOF
echo "Schritt 7 erledigt"
echo "Step 7 done"
echo "------------------"
</pre>
<br />
In Step 8, the network interface of the machine gets reconfigured due to problems that vagrant might have by setting a static IP address for a host.only network.<br />
Assigning the desired IP via Vagrant's network definition failed for me. After reading through a lot of Stackoverloads, I ended up using the network manager cli to reconfigure the network adapter manually. This proved to work out more reliably for me:<br />
<pre class="brush: bash">echo "Schritt 8: network interface manuell konfigurieren"
echo "Step 8: manually configure the network adapter"
echo "=================================================="
sudo nmcli con mod "Wired connection 1" ipv4.address "192.168.2.10/24"
sudo nmcli con mod "Wired connection 1" ipv4.method "manual"
sudo nmcli con down "Wired connection 1"
sudo nmcli con up "Wired connection 1"
echo "Schritt 8 erledigt"
echo "Step 8 done"
echo "------------------"
</pre>
<br />
Finally we are getting closer to installing Kubernetes itself.<br />
In Step 9 we install the kubeadm package and start the kubelet service on our master node:<br />
<pre class="brush: bash">echo "Schritt 9: kubeadm installieren und starten"
echo "Step 9: install and start kubeadm"
echo "==========================================="
sudo yum install -y kubeadm
echo "kubelet Dienst starten"
sudo systemctl enable kubelet && systemctl start kubelet
echo "Schritt 9 erledigt"
echo "Step 9 done"
echo "------------------"
</pre>
<br />
Step 10 just pulls down some images that kubeadm needs later on anyway:<br />
<pre class="brush: bash">echo "Schritt 10: schon mal die wichtigsten Images pullen"
echo "Step 10: pulling down some images for later"
echo "==================================================="
kubeadm config images pull
echo "Schritt 10 erledigt"
echo " Step 10 done"
echo "------------------"
</pre>
<br />
Step 11 is the heart of the matter. We initialize the master node using the "kubeadm init" command.<br />
If all goes well (after all we already did is should) kubeadm will output a "join" command for adding worker nodes to the master node just initialized.<br />
Copy this command and save it somewhere. It is needed for adding worker nodes (see below).<br />
<pre class="brush: bash">echo "Schritt 11: Master Node initialisieren"
echo "Step 11: Initialize master node"
echo "======================================"
sudo kubeadm init --apiserver-advertise-address=192.168.2.10 --pod-network-cidr=10.244.0.0/16
echo "Schritt 11 erledigt: Achtung - den 'kubeadm join' muss man sich sichern!"
echo "Step 11 done: important - you must save the kubeadm join command for joining nodes later!"
echo "------------------"
</pre>
<br />
Just two steps left!<br />
Please note that these are preformed as user "vagrant".<br />
<br />
Step 12 creates a ".kube" directory under the vagrant home and copies the cluster configuration into this directory.<br />
<pre class="brush: bash">echo "Schritt 12: Konfiguration ins Home-Verzeichnis von vagrant kopieren"
echo " Step 12: copy configuration in vagrant's home directory"
echo "==================================================================="
# Diese Schritte werden unter dem User vagrant durchgeführt
# these steps will be performed as user vagrant
sudo -i -u vagrant bash << EOF
mkdir -p /home/vagrant/.kube
sudo cp -i /etc/kubernetes/admin.conf /home/vagrant/.kube/config
sudo chown vagrant:vagrant /home/vagrant/.kube/config
echo "Schritt 12 erledigt"
echo "Step 12 done"
echo "-------------------"
</pre>
<br />
Step 13 (the final step for a master node) installs the Flannel Overlay Network on the master node.<br />
This network will automatically extend to all subsequently added worker nodes.<br />
<pre class="brush: bash">echo "Schritt 13: Overlay Netzwerk installieren (wir verwenden Flannel)"
echo "Step 13: installing overlay network (we are using Flannel here)"
echo "================================================================="
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml
echo "Schritt 13 erledigt"
echo "Step 13 done"
echo "-------------------"
EOF
SHELL
</pre>
<br />
Now you know what is going on behind the scenes, when you do the innocent "vagrant up" for a master node the first time.<br />
Because this is a shell provisioner, these are all regular shell commands which you could run manually instead of having Vagrant execute them.<br />
<br />
Let's take a look at the vagrantfile for a worker node.<br />
<h3>
Provisioning a Worker Node</h3>
The vagrantfile for a worker node is more or less identical to the master node, except that it ends after Step 10.<br />
<br />
The only other differences are:<br />
<br />
<ul>
<li>give the vm a different name: cl2node1</li>
<li>give the host a different hostname: cl2node1</li>
<li>give it a different IP: 192.168.2.11</li>
</ul>
<br />
When this is done, just switch into the correct directory, do the "vagrant up", ssh into the machine and execute the join command you have copied and saved earlier.<br />
<h2>
Disclaimer</h2>
<div>
<div style="margin: 0px;">
<div style="color: black; font-family: "times new roman"; font-size: medium; font-style: normal; font-weight: 400; letter-spacing: normal; text-transform: none; white-space: normal; word-spacing: 0px;">
The descriptions above worked for me every time up to the time of this writing. Nevertheless I can't take any responsibility if something might not work for you now or in the future.</div>
<div style="color: black; font-family: "times new roman"; font-size: medium; font-style: normal; font-weight: 400; letter-spacing: normal; text-transform: none; white-space: normal; word-spacing: 0px;">
</div>
<div style="color: black; font-family: "times new roman"; font-size: medium; font-style: normal; font-weight: 400; letter-spacing: normal; text-transform: none; white-space: normal; word-spacing: 0px;">
<br />
<br />
See also:</div>
<ul>
<li><span style="font-family: "times new roman";"><a href="https://jeeonthepi.blogspot.com/2018/09/local-kubernetes-cluster-on-centos7.html" target="_blank">Part I</a> (installing "out of the box" by cloning the repo)</span></li>
<li><span style="font-family: "times new roman";">Part III (modifying the vagrantfiles and adding a 4th node)</span></li>
</ul>
</div>
</div>
<div style="color: black; font-family: "times new roman"; font-size: medium; font-style: normal; font-weight: 400; letter-spacing: normal; text-transform: none; white-space: normal; word-spacing: 0px;">
<div style="margin: 0px;">
</div>
</div>
<div style="color: black; font-family: "times new roman"; font-size: medium; font-style: normal; font-weight: 400; letter-spacing: normal; margin: 0px; text-transform: none; white-space: normal; word-spacing: 0px;">
</div>
Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com3tag:blogger.com,1999:blog-1625776533566626066.post-49686684154003968372018-09-17T22:00:00.001+02:002018-11-03T12:55:57.852+01:00Local Kubernetes Cluster on CentOS7 with Vagrant and VirtualBox on your Windows Laptop Part I<h2>
Updates</h2>
<h3>
03.11.2018</h3>
<div>
The Vagrantfile has been updated due to changes in Kubernetes` documentation:</div>
<div>
<ul>
<li>now adding Docker-CE Repository and installing Docker-CE explicitly</li>
<li>minor changes of adding Kubeadm-Repo</li>
<li>Kubernetes and Docker now using cgroupfs cgroup driver (instead of systemd)</li>
<li>New URL to install and configure Flannel Overly Network</li>
</ul>
<div>
Check out the new Vagrantfiles <a href="https://github.com/vkoster/k8s-local.git" target="_blank">here</a> or perform the steps from the updated shell provisioner manually.</div>
</div>
<div>
<br /></div>
<div>
Without these changes the master node didn't reach state "ready" any more, due to network problems.</div>
<div>
Now it works like a charm again.</div>
<h2>
What this post is about</h2>
<div>
This post is about setting up a real kubernetes cluster consisting of 1 master node and as many worker nodes as you like (or your machine can spin up) within a local network on your laptop.<br />
<br />
You can easily spin up a kubernetes minkube on your machine, but may be you prefer something a little bit closer to the real thing, like running each node on a separate VM connected via some private network within your host.<br />
<br />
I promise that setting up a cluster of consisting of one master node and two worker nodes will be as easy as using minikube.<br />
<br />
This post consists of three parts:<br />
<ol>
<li>Set up the cluster "out of the box" (this post)</li>
<li>Explaining the Vagrantfiles for master and workers in detail</li>
<li>Customizing the Vagrantfiles </li>
</ol>
</div>
<div>
<h2>
What you need up front</h2>
<div>
I'm doing this on my Windows 10 machine using VirtualBox for spinning up the VMs that constitute the cluster nodes and Vagrant to automate VirtualBox.</div>
<div>
All nodes will be running CentOS 7</div>
</div>
<div>
<br /></div>
<div>
On your Windows machine, you need to have a virtualization software running. I'm using VirtualBox.</div>
<div>
Additionally you need to have Vagrant installed.</div>
<div>
<br /></div>
<div>
In case you do not have these installed already, they are really easy to get. Go to their respective download sites, download the installers and install first VirtualBox, then Vagrant. I'm using this combination for years and never encountered any serious problems.</div>
<h2>
Why Vagrant?</h2>
<div>
Using Vagant, we can automate the process of provisioning the nodes.</div>
<div>
Once we have created the "Vagrantfile" for the master node, one command, "vagrant up", will spin up VM running the master.<br />
Each worker node we want to add, has its respective and even less complex Vagrantfile in another directory and can be started with another "vagrant up" respectivly.<br />
<br />
All provisioning is done within the vagrantfiles for master and workers respectively.<br />
Every step performed there, will be explained below.<br />
<br />
<h2>
Part I: Setting Up The Cluster "As Is"</h2>
<h3>
Check Your Environment</h3>
<div>
As mentioned you need VirtualBox (or some other virtualization tool) and Vagrant running on your machine.</div>
<div>
To check your environment, open a shell (I'm using Git Bash in all examples) and type "vagrant version":<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://3.bp.blogspot.com/-V7FnjTBzBXU/W6AHbxrspeI/AAAAAAAAAPw/fnPWRtG0tTQ8RapV9w0Iic6NUSE8tIrCgCLcBGAs/s1600/check_vagrant_new.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="467" data-original-width="1131" height="132" src="https://3.bp.blogspot.com/-V7FnjTBzBXU/W6AHbxrspeI/AAAAAAAAAPw/fnPWRtG0tTQ8RapV9w0Iic6NUSE8tIrCgCLcBGAs/s320/check_vagrant_new.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Check Vagrant Version</td></tr>
</tbody></table>
<br /></div>
<div>
To check your VirtualBox just open the GUI.</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://4.bp.blogspot.com/-72Yk4TOlj-c/W4J-lIyIchI/AAAAAAAAAPk/5hy1Bu_Sl3MTuAyxZk-Wl_tu8qD93CthgCLcBGAs/s1600/VirtualBox.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" data-original-height="1271" data-original-width="1600" height="254" src="https://4.bp.blogspot.com/-72Yk4TOlj-c/W4J-lIyIchI/AAAAAAAAAPk/5hy1Bu_Sl3MTuAyxZk-Wl_tu8qD93CthgCLcBGAs/s320/VirtualBox.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">About VirtualBox</td></tr>
</tbody></table>
<div>
You can use "Check for Updates" in order to make sure you are on the latest release.</div>
<div>
<br /></div>
<h3>
Get the Project</h3>
<div>
Open your Git Bash and create a directory for the project.</div>
<div>
For example, let's create the directory "mycluster":</div>
<pre class="brush: java"># create the "mycluster" directory:
$ mkdir /c/mycluster
$ cd /c/mycluster
</pre>
<div>
<br />
Clone the project from GitHub.<br />
This will get you the directories for the master node and tow worker nodes containing the respective Vagrantfiles.<br />
<pre class="brush: java"># clone from GitHub:
$ git clone https://github.com/vkoster/k8s-local.git
Cloning into 'k8s-local'...
remote: Counting objects: 29, done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 29 (delta 10), reused 29 (delta 10), pack-reused 0
Unpacking objects: 100% (29/29), done.
</pre>
</div>
<div>
<br />
Change into the "k8s-local" directory and check what you got:<br />
<pre class="brush: java"># see what you got:
$ ls -al
total 24
drwxr-xr-x 1 vkoster 1049089 0 Sep 15 23:49 ./
drwxr-xr-x 1 vkoster 1049089 0 Sep 15 23:49 ../
drwxr-xr-x 1 vkoster 1049089 0 Sep 15 23:49 k8s-local/
$ cd k8s-local
$ ls -al
total 14
drwxr-xr-x 1 vkoster 1049089 0 Sep 15 23:49 ./
drwxr-xr-x 1 vkoster 1049089 0 Sep 15 23:49 ../
drwxr-xr-x 1 vkoster 1049089 0 Sep 15 23:49 .git/
-rw-r--r-- 1 vkoster 1049089 96 Sep 15 23:49 .gitignore
drwxr-xr-x 1 vkoster 1049089 0 Sep 15 23:49 cl2master/
drwxr-xr-x 1 vkoster 1049089 0 Sep 15 23:49 cl2nfs/
drwxr-xr-x 1 vkoster 1049089 0 Sep 15 23:49 cl2node1/
drwxr-xr-x 1 vkoster 1049089 0 Sep 15 23:49 cl2node2/
-rw-r--r-- 1 vkoster 1049089 897 Sep 15 23:49 README.md
-rw-r--r-- 1 vkoster 1049089 55 Sep 15 23:49 TODO.md
</pre>
<br />
We are interested in these directories:<br />
<br />
<ul>
<li>cl2master<br />this is where the master node will be created</li>
<li>cl2node1 and cl2node2<br />this is where the worker nodes will be created</li>
</ul>
<div>
(Ignore the cl2nfs directory for now. Its part of another post)</div>
<br />
<h3>
Create the Master Node</h3>
</div>
<div>
Let's create the master node. Change into the master node directory and do the "vagrant up":</div>
<pre class="brush: java"># creating the master node:
$ cd cl2master
$ vagrant up
...
*** a lot of terminal output ***
...
default:
default: Your Kubernetes master has initialized successfully!
default:
default: To start using your cluster, you need to run the following as a regular user:
default:
default: mkdir -p $HOME/.kube
default: sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
default: sudo chown $(id -u):$(id -g) $HOME/.kube/config
default:
default: You should now deploy a pod network to the cluster.
default: Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
default: https://kubernetes.io/docs/concepts/cluster-administration/addons/
default:
default: You can now join any number of machines by running the following on each node
default: as root:
default:
default: kubeadm join 192.168.2.10:6443 --token 14q1vx.z9nxyuc49e9v7rot --discovery-token-ca-cert-hash sha256:c9f757102120d5f776ddb05ac533ee3fb5e5006773f81953ed4753e8000da775
default: Schritt 11 erledigt: Achtung - den 'kubeadm join' muss man sich sichern!
default: Step 11 done: important - you must save the kubeadm join command for joining nodes later!
default: ------------------
default: Schritt 12: Konfiguration ins Home-Verzeichnis von vagrant kopieren
default: Step 12: copy configuration in vagrant's home directory
default: ===================================================================
default: Schritt 12 erledigt
default: Step 12 done
default: -------------------
default: Schritt 13: Overlay Netzwerk installieren (wir verwenden Flannel)
default: Step 13: installing overlay network (we are using Flannel here)
default: =================================================================
default: clusterrole.rbac.authorization.k8s.io/flannel created
default: clusterrolebinding.rbac.authorization.k8s.io/flannel created
default: serviceaccount/flannel created
default: configmap/kube-flannel-cfg created
default: daemonset.extensions/kube-flannel-ds created
default: Schritt 13 erledigt
default: Step 13 done
default: -------------------
</pre>
<div>
<br /></div>
<div>
Now you need a little patience.<br />
Vagrant will bring up the machine, update it, prepare it for running as a Kubernetes Master Node and finally install and run Kubernetes.<br />
You can track what is going on by checking the steps outlined in the Vagrantfile.<br />
<br />
During Step 11 Kubernetes will output "join command" for joining subsequent nodes to the cluster. You should copy and save this somewhere. You will need this command and the join-token to add worker nodes to the cluster later on.<br />
<br />
Looking at the output of the last step (Step 13), you should be able to verify that the Flannel overlay network was installed successfully. When joining nodes to the cluster, the overlay network will be extended to those new nodes automatically.<br />
<br />
Ok, you now have Kubernetes Master Node running on your machine:<br />
<br />
<ul>
<li>hostname: cl2master</li>
<li>ip: 192.168.2.10</li>
<li>running in a private 192.168.2.0/24 network</li>
</ul>
<div>
Let's check if everything is working fine by ssh-ing into the VM and listing the Pods running in the "kube-system" namespace:</div>
</div>
<pre class="brush: java"># see that the master node is working:
$ vagrant ssh
[vagrant@cl2master ~]$
# you are now inside of your new vagrant box...
# check the "kube-system" namespace:
$ kubectl get pods --namespace kube-system
NAME READY STATUS RESTARTS AGE
coredns-78fcdf6894-hhmgb 1/1 Running 0 18h
coredns-78fcdf6894-j5mkx 1/1 Running 0 18h
etcd-cl2master 1/1 Running 0 18h
kube-apiserver-cl2master 1/1 Running 0 18h
kube-controller-manager-cl2master 1/1 Running 0 18h
kube-flannel-ds-dxgc8 1/1 Running 0 18h
kube-proxy-tmtwc 1/1 Running 0 18h
kube-scheduler-cl2master 1/1 Running 0 18h
</pre>
<div>
<br />
Apart from the AGE column, your output should be something like the above.
<br />
Type "exit" to leave the box for now and find yourself in your GitBash again.</div>
<div>
<br /></div>
<div>
A Master Node is quite useless by its own because without taking explicite action, Kubernetes will not schedule your deployments on a Master Node. So let's join two Worker Nodes.<br />
<h3>
Create and Join Two Worker Nodes</h3>
</div>
<div>
Simple change into the "cl2node1" directory and again type "vagrant up":</div>
<pre class="brush: java"># create the first worker node
$ cd ../cl2node1/
$ ls -al
total 12
drwxr-xr-x 1 vkoster 1049089 0 Sep 15 23:49 ./
drwxr-xr-x 1 vkoster 1049089 0 Sep 15 23:49 ../
-rw-r--r-- 1 vkoster 1049089 5492 Sep 15 23:49 Vagrantfile
# now the "vagrant up":
$ vagrant up
...
default: Complete!
default: kubelet Dienst starten
default: Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /etc/systemd/system/kubelet.service.
default: Schritt 9 erledigt
default: Step 9 done
default: ------------------
default: Schritt 10: schon mal die wichtigsten Images pullen
default: Step 10: pulling down some images for later
default: ===================================================
default: [config/images] Pulled k8s.gcr.io/kube-apiserver-amd64:v1.11.3
default: [config/images] Pulled k8s.gcr.io/kube-controller-manager-amd64:v1.11.3
default: [config/images] Pulled k8s.gcr.io/kube-scheduler-amd64:v1.11.3
default: [config/images] Pulled k8s.gcr.io/kube-proxy-amd64:v1.11.3
default: [config/images] Pulled k8s.gcr.io/pause:3.1
default: [config/images] Pulled k8s.gcr.io/etcd-amd64:3.2.18
default: [config/images] Pulled k8s.gcr.io/coredns:1.1.3
default: Schritt 10 erledigt
default: Step 10 done
default: ------------------
</pre>
<div>
Setting up a Worker Node is done after completing step 10 of the Vagrantfile.
You should see something like the above at the end of the terminal output.
No we have to make this node join the cluster.
You do this by running the join-command generated by Kubernetes while setting up the Master Node:
</div>
<pre class="brush: java"># joining this node to the cluster
$ sudo kubeadm join 192.168.2.10:6443 --token 14q1vx.z9nxyuc49e9v7rot --discovery-token-ca-cert-hash sha256:c9f757102120d5f776ddb05ac533ee3fb5e5006773f81953ed4753e8000da775
...
This node has joined the cluster:
* Certificate signing request was sent to master and a response
was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the master to see this node join the cluster.
</pre>
<div>
You should see something like this at the end of your terminal output.
At the end, Kubernetes tells us how to check whether this worked.
To do this, exit the worker node vm, re-enter the Master Node directory, ssh into the box and run the "get nodes" command with kubctl:
</div>
<pre class="brush: java"># exit vm
$ exit
# now back in GitBash
$ cd ../cl2master/
# enter master node
$ vagrant ssh
# now inside master vm
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
cl2master Ready master 22h v1.11.3
cl2node1 Ready <none> 21m v1.11.3
</none></pre>
<div>
Type "exit" to leave the master and enter GitBash again.
<br />
OK, now we have a cluster consisting of one master and one worker node.
Adding the second worker node is exactly the same procedure as adding the fist.
Just make sure to change into the "cl2node2" directory:
</div>
<pre class="brush: java"># now in GitBash - cl2master directory...
$ cd ../cl2node2
# do the "vagrant up"
$ vagrant up
... lots of noise...
...
default: Complete!
default: kubelet Dienst starten
default: Created symlink from /etc/systemd/system/multi-user.target.wants/kubelet.service to /etc/systemd/system/kubelet.service.
default: Schritt 9 erledigt
default: Step 9 done
default: ------------------
default: Schritt 10: schon mal die wichtigsten Images pullen
default: Step 10: pulling down some images for later
default: ===================================================
default: [config/images] Pulled k8s.gcr.io/kube-apiserver-amd64:v1.11.3
default: [config/images] Pulled k8s.gcr.io/kube-controller-manager-amd64:v1.11.3
default: [config/images] Pulled k8s.gcr.io/kube-scheduler-amd64:v1.11.3
default: [config/images] Pulled k8s.gcr.io/kube-proxy-amd64:v1.11.3
default: [config/images] Pulled k8s.gcr.io/pause:3.1
default: [config/images] Pulled k8s.gcr.io/etcd-amd64:3.2.18
default: [config/images] Pulled k8s.gcr.io/coredns:1.1.3
default: Schritt 10 erledigt
default: Step 10 done
default: ------------------
# enter the new node via ssh to run the "Join-Command":
$ vagrant ssh
# now inside the new node...
$ sudo kubeadm join 192.168.2.10:6443 --token 14q1vx.z9nxyuc49e9v7rot --discovery-token-ca-cert-hash sha256:c9f757102120d5f776ddb05ac533ee3fb5e5006773f81953ed4753e8000da775
...
This node has joined the cluster:
* Certificate signing request was sent to master and a response
was received.
* The Kubelet was informed of the new secure connection details.
Run 'kubectl get nodes' on the master to see this node join the cluster.
</pre>
<div>
Let's check again on the master node:
</div>
<pre class="brush: java"># exit the node2-box:
$ exit
# change into master node directory
$ cd ../cl2master/
# enter the master node via ssh
$ vagrant ssh
# now inside master node vm...
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
cl2master Ready master 23h v1.11.3
cl2node1 Ready <none> 1h v1.11.3
cl2node2 Ready <none> 32m v1.11.3
</none></none></pre>
<br />
<h2>
Summary</h2>
<div>
Now you habe a Kubernetes cluster consisting of one master and two worker nodes running on your lokal machine.
The respective VMs are configured like so:
<br />
<br />
<ul>
<li>master node</li>
<ul>
<li>hostname: cl2master</li>
<li>ip: 192.168.2.10</li>
</ul>
<li>worker node 1</li>
<ul>
<li>hostname: cl2node1</li>
<li>ip: 192.168.2.11</li>
</ul>
<li>worker node 2</li>
<ul>
<li>hostname: cl2node2</li>
<li>ip: 192.168.2.12</li>
</ul>
</ul>
<div>
The next post will discuss the Vagrantfiles for master and worker nodes respectively.</div>
</div>
<h2>
Disclaimer</h2>
<div>
The descriptions above worked for me every time up to the time of this writing. Nevertheless I can't take any responsibility if something might not work for you now or in the future.<br />
<br />
<br />
See also:<br />
<br />
<ul>
<li><a href="https://jeeonthepi.blogspot.com/2018/10/local-kubernetes-cluster-on-centos7.html" target="_blank">Part II</a> (Setup Steps explained)</li>
</ul>
</div>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com0tag:blogger.com,1999:blog-1625776533566626066.post-78691362795494461182016-11-11T08:21:00.000+01:002018-08-03T13:42:29.487+02:00Docker Tag Re-Assignment and the "latest" Tag<h2>
Docker Tag Re-Assignment and the „latest“ Tag</h2>
<div class="MsoNormal">
<o:p></o:p></div>
<h3>
<span lang="EN-US">Introduction</span></h3>
<div class="MsoNormal">
<span lang="EN-US">The Docker
latest tag can be a bit of mystery.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US">The
confusion is mostly based on the legitimate assumption that an image tagged “latest” is the last and therefore newest image added to a given repository.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">This
assumption, legitimate or not, is definitely wrong. The tag “lastest” is just a
token like “0.1.1” or “iojdfh”.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span>
<span lang="EN-US">The only
thing that makes "latest" special is that Docker assumes it as a default value for
its “docker commit” or “docker tag” commands in case you do not specify a tag explicitely.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US">The following links cover this in great detail.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US">Marc
Campbell: <a href="http://container-solutions.com/docker-latest-confusion/" target="_blank">The misunderstood Docker tag: latest</a></span></div>
<div class="MsoNormal">
<span lang="EN-US">Adrian
Mouat: <a href="https://medium.com/@mccode/the-misunderstood-docker-tag-latest-af3babfd6375" target="_blank">Docker: The latest Confusion</a></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US">Nothing
left to add to these.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US">What I want
to shed some light on here, is:<o:p></o:p></span></div>
<div class="MsoNormal">
</div>
<ul>
<li>how Docker treats
tags as pointers to images</li>
<li>how Docker distinguishes
between creating and re-assigning tags</li>
</ul>
<br />
As always,
if you know all that then don’t waste your time and stop reading now.<br />
If you
are not so sure, keep going. I admit that some details of tagging have been a
surprise to me.<br />
<div class="MsoNormal">
<br />
<h2>
Setup</h2>
</div>
<div class="MsoNormal">
<span lang="EN-US">Let’s start
out with creating a setup just rich enough to see the mechanics. <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Creating a
repository containing two versioned images will just do it.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span>
<span lang="EN-US">Please note
upfront:<o:p></o:p></span></div>
<div class="MsoNormal">
</div>
<ul>
<li>I’m on Windows 10, running my docker in Vagrant powered CentOS 7 VirtualBox</li>
<li>I’m working locally, not
pushing to or pulling from Docker Hub (except when running the base image)</li>
<li>I’m not
using a dockerfile but create my images by committing containers</li>
</ul>
<br />
<div class="MsoNormal">
<span lang="EN-US">Now let’s
create the images:<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<pre class="brush: java">#
# Run Container based on base image „centos“:
$ docker run -it centos /bin/bash
#
</pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">This will
start an interactive container running a bash shell and You will find yourself
within this shell.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Create a file “/test.sh”, e.g. using vi, and enter this content:<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<pre class="brush: java">#
#!/usr/bin/env bash
echo "Hello, I am version 01"
</pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">Now exit
the container and commit it to a version 1 image:<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<pre class="brush: java">#
$ docker commit -a "volker.koster@mt-ag.com" -m "Added /test.sh" \
-c 'CMD ["/bin/bash", "/test.sh"]' 333ba1982dbe vkoster/versionen:v01
#
</pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">Things to
note here:<o:p></o:p></span></div>
<div class="MsoNormal">
<ul>
<li>333ba1982dbe
is the container’s id</li>
<li>The
--change option makes this container an executable that just runs the /test.sh
file</li>
<li>vkoster/versionen
is the name of the repository for our images</li>
<li>v01 is our
version tag</li>
</ul>
</div>
<div class="MsoNormal">
<span lang="EN-US">Had we not
specified the version tag, docker would have assigned “latest” as a default
tag. Told differently, docker did not assign the “latest” tag because we
specified a tag explicitely.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span>
<span lang="EN-US">Now list
the images and run a container to see if it works:<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<pre class="brush: java">#
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
vkoster/versionen v01 51e388437466 26 seconds ago 196.7 MB
$ docker run vkoster/versionen:v01
Hello, I am version 01
#
</pre>
<div class="MsoNormal">
(Omitting the "v01" tag now, would have caused an error, because Docker would then assume "lates", which is not present)<br />
<br />
Image works. Let’t tag it as “latest”:</div>
<div class="MsoNormal">
<br /></div>
<pre class="brush: java">#
$ docker tag vkoster/versionen:v01 vkoster/versionen:latest
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
vkoster/versionen latest 51e388437466 10 minutes ago 196.7 MB
vkoster/versionen v01 51e388437466 10 minutes ago 196.7 MB
#
</pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">We now have
one repository containing one image referenced by two tags.<o:p></o:p></span><br />
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US">Now let's create
version 2 of this image.<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<pre class="brush: java">#
$ docker run -it vkoster/versionen /bin/bash
#
</pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span>
<span lang="EN-US">(Now it is ok to omit the tag: Docker assumes "latest", which is now present)</span><br />
<span lang="EN-US"><br /></span>
<span lang="EN-US">Within the
running container, edit the /test.sh file like so:<o:p></o:p></span></div>
<pre class="brush: java">#
#!/usr/bin/env bash
echo "Hello, I am version 02"
#
</pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">Save the
file, exit the container and again commit it to an image:<o:p></o:p></span></div>
<pre class="brush: java">#
$ docker commit -a "volker.koster@mt-ag.com" -m "Changed /test.sh" \
-c 'CMD ["/bin/bash", "/test.sh"]' 5f1f1063fd2a vkoster/versionen:v02
#
</pre>
<div class="MsoNormal">
List the
images and run the new version, just to be sure.</div>
<pre class="brush: java">#
$ docker images -a
REPOSITORY TAG IMAGE ID CREATED SIZE
vkoster/versionen v02 435bb586c058 5 minutes ago 196.7 MB
vkoster/versionen latest 51e388437466 57 minutes ago 196.7 MB
vkoster/versionen v01 51e388437466 57 minutes ago 196.7 MB
# …and run the image:
$ docker run vkoster/versionen:v02
Hello, I am version 02
#
</pre>
<div class="MsoNormal">
We still have
one repository, now containing two images.<br />
As was expected and covered in the
links above, the “latest” tag did not move but still references the
first image.<br />
<br />
Now we can play around with the tags a bit.</div>
<div class="MsoNormal">
<h3>
<span lang="EN-US">Questions</span></h3>
</div>
<div class="MsoNormal">
<span lang="EN-US"><b>Question</b>:
what will happen, if we try to tag the new image with the “latest” tag? </span><br />
<span lang="EN-US">Obviously there cannot be two “latest” tags in one repo.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span>
<span lang="EN-US">Tag the new
image with “latest” and display the results:<o:p></o:p></span></div>
<pre class="brush: java">#
$ docker tag vkoster/versionen:v02 vkoster/versionen:latest
$ docker images –a
REPOSITORY TAG IMAGE ID CREATED SIZE
vkoster/versionen latest 435bb586c058 9 minutes ago 196.7 MB
vkoster/versionen v02 435bb586c058 9 minutes ago 196.7 MB
vkoster/versionen v01 51e388437466 About an hour ago 196.7 MB
#
</pre>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">As it
worked, this is what happened:</span></div>
<div class="MsoNormal">
<ul>
<li>we tried
assign the “latest” tag</li>
<li>Docker
recognizes that the repository already contains the tag “latest”</li>
<li>therefore,
the tag is not created but simply re-assigned to point to the new image</li>
<li>there is
only one pointer left, “v01”, to point to the first image</li>
</ul>
</div>
<div class="MsoNormal">
<span lang="EN-US"><b><br /></b></span>
<span lang="EN-US"><b>Question</b>:
Are all tags treated like this or is the “latest” tag special?<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span>
<span lang="EN-US">This works
for all tags. Again, the tag “latest” is in no way special.<o:p></o:p></span></div>
<div class="MsoNormal">
You can try this yourself by re-tagging “v01” or “v02”.<o:p></o:p><br />
<br /></div>
<div class="MsoNormal">
<span lang="EN-US"><b>Question</b>:
What happens when we remove the last tag from an image?<o:p></o:p></span></div>
<pre class="brush: java">#
$ docker tag vkoster/versionen:latest vkoster/versionen:v01
$ docker images -a
REPOSITORY TAG IMAGE ID CREATED SIZE
vkoster/versionen latest 435bb586c058 57 minutes ago 196.7 MB
vkoster/versionen v01 435bb586c058 57 minutes ago 196.7 MB
vkoster/versionen v02 435bb586c058 57 minutes ago 196.7 MB
<none> <none> 51e388437466 About an hour ago 196.7 MB
#
</pre>
<br />
<div class="MsoNormal">
Docker re-assigned the pointer with no problem at all, leaving our fist image “naked”.<br />
This image can no longer be referenced via the repository.</div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<h3>
<span lang="EN-US">Takeway</span></h3>
</div>
<div class="MsoNormal">
The results can be summed up like this:<br />
<ul>
<li>tags are
just pointers to images</li>
<li>when
tagging, docker checks, if the respective tag is already present within the
repository</li>
<ul>
<li>if the tag
is not present, it is created, pointing to the respective image</li>
<li>if the tag
is present, it is simply re-assigned from one image to another</li>
</ul>
<li>this works
for all tags; there is no special treatment for “latest” whatsoever</li>
</ul>
</div>
<br />
<div class="MsoNormal">
<br /></div>
Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com0tag:blogger.com,1999:blog-1625776533566626066.post-50946453725436077082016-02-15T21:13:00.002+01:002016-02-15T22:31:35.113+01:00NodeJS JavaScript Oracle Database Development in a Vagrant powered VirtualBox<h2>
1 Abstract</h2>
<div>
<div>
This post describes the process of installing OracleXE, NodeJS and NodeJS Oracle Driver “oracledb” into a Vagrant powered VirtualBox with Linux as Guest OS running on Windows7 as Host OS.</div>
</div>
<div>
<h2>
2 Introduction</h2>
</div>
<div>
<div>
This is not on the Raspberry Pi and not about CouchDB.</div>
<div>
Instead this is about what I experienced in a real life project dealing with NodeJS Oracle-DB development on a Windows machine within a Vagrant powered VirtualBox running Linux.</div>
<div>
<br /></div>
<div>
I would like to share here some of the hurdles we had to overcome In order to make this setup work properly. Hopefully this will help you save some time in case you plan something likewise.</div>
</div>
<div>
<h2>
3 Some Background</h2>
</div>
<div>
<div>
We started off with the idea to try something different and implement our services as NodeJS-Express modules using Oracle DB for persistence. A first prototype of the software was supposed to be implemented on a Windows developer notebook.</div>
<div>
Oracle maintains a NodeJS driver for Oracle DB which can be installed via the Npm repository.</div>
<div>
As it turned out, this installation is not as trivial as it sounds. During installation, the module has to be built locally which of course requires some tooling to be present on your machine. Some research (Google) told us that a specific version of Visual Studio combined with a certain .Net version would be required to get the task done.</div>
<div>
Frankly speaking and without wanting to hurt anyone’s feelings, I did not want to have this on my machine. I’m using Windows 7 and given the current state of my machine, I was not sure if I would ever be able to install the required components in their respective versions.</div>
<div>
The idea was born to bring up a Vagrant powered VirtulaBox running Linux and to install OracleXE, NodeJS and the oracledb driver into this box. Good enough for a prototype and much easier to share and discuss with others.</div>
<div>
<br /></div>
<div>
The rest of the blog deals with the hurdles to jump while setting up this environment making the assumption that you have Vagrant and VirtualBox already installed on your Windows machine. </div>
<div>
Talking about hurdles I should mention that you may well get away with this without stumbling over anything at all. Don’t sue me for wasting your time.</div>
<div>
On the other hand you might come across pitfalls not mentioned here. In this case I would appreciate a note which I will compile to the list.</div>
<div>
<br /></div>
<div>
Let’s start.</div>
</div>
<div>
<br /></div>
<div>
<div>
The whole process consists of two very distinct tasks/ projects:</div>
<div>
<ul>
<li>Engineering Task: create a Vagrant Base Box containing OracleXE and NodeJS and prepare it for installing the oracledb driver</li>
<li>Development Task: Create a NodeJS project using your new Vagrant Base Box and actually install the oracledb driver into this project</li>
</ul>
</div>
<div>
Our goal is to make the Development Task as easy as “npm install oracledb”, so most of our efforts will be invested into the Engineering Task.</div>
</div>
<div>
<h2>
4 Engineering Task: Building your Vagrant Base Box</h2>
<h3>
4.1 Selecting a Vagrant Base Box to start with</h3>
<div>
You can save a lot of time just by choosing an appropriate Vagrant Base image for the task at hand.</div>
<div>
Oracle distributes its OralceXE database on Linux as an RPM package. RedHat Linux, the binary compatible CentOS or Fidora use RPM as their package manager.</div>
<div>
If you insist to install on Debian, Ubuntu or the like, you have to use tools like “alien” to install from an RPM package. We found this to unnecessarily complicate the installation process. Even after a successful installation, quite some effort has to be invested in getting OracleXE running on Ubuntu. A well written and working description of the process can be found <a href="http://meandmyubuntulinux.blogspot.de/2012/05/installing-oracle-11g-r2-express.html" target="_blank">here</a>.</div>
<div>
<br /></div>
<div>
Anyway, we choose CentOS7 64Bit as our Guest OS.</div>
<div>
Researching the <a href="https://atlas.hashicorp.com/boxes/search" target="_blank">Atlas-Hashicorp Vagrant Repository</a>, we found the "geerlingguy/centos7" box to fit or needs. It contains a minimal CentOS 7 64 Bit.</div>
<div>
<br /></div>
<div>
On your Windows machine create the root directory of your engineering project (called <eng_root> from now on).</div>
<div>
I use my Windows Git-Bash instead of a Windows command shell so that I can use Linux commands inside and outside of my Vagrant Box.</div>
<div>
<br /></div>
<pre class="brush: java"># in your HostOS (Windows) but in Git-Bash...
# create project home directory:
mkdir <eng_root>
cd <eng_root>
#
# let Vagrant create a Vagrantfile for you:
vagrant init –m geerlingguy/centos7
#
# start up the box
vagrant up
#</pre>
<div>
The “vagrant init” command will create an initial version of your “Vagrantfile”. The Vagrantfile will contain enough information to download the Vagrant Base Box, expand it into a VirtualBox image and load it into VirtualBox. Later in the process we will add some configuration to the Vagrantfile.</div>
<div>
<br /></div>
<div>
“vagrant up” makes Vagrant download the Base Box, import it into VirtualBox and start the VM.</div>
<div>
This may take a while because of the download. Once downloaded, Vagrant will use your local copy on every subsequent “vagrant up”.</div>
<div>
<br /></div>
<div>
Vagrant mounts your project's root directory <eng_root> as a shared directory under "/vagrant" within the VirtualBox. “Shared directory” means that you can access this directory and all its sub-directories from your HostOS (Windows) and from within your VM, as you please (once you have ssh-ed into the VM that is).</div>
</div>
<div>
<h3>
4.2 Check some preconditions</h3>
</div>
<div>
<div>
Your Vagrant Box has to meet some prerequisites for a successful installation of the oracle-node driver later in the development project, due to the fact that the driver has to be built during installation.</div>
<div>
We found the following preconditions to be essential for a successful build of oracledb inside of our vagrant box:</div>
</div>
<div>
<h4>
4.2.1 Version of binutils</h4>
</div>
<div>
<div>
While trying to install the “oracledb” NodeJS driver, we had this error:</div>
<blockquote class="tr_bq">
Error: expecting string instruction after `rep'» in code w/o inline assembly</blockquote>
<div>
It could be traced back to the version of the “binutils” library.</div>
<div>
Make sure that your box contains this lib in version of 2.23.52.0.1 or better.</div>
<div>
Our CentOS7 box meets this prerequisite:</div>
</div>
<div>
<br /></div>
<div>
<pre class="brush: java">#
$ sudo yum info binutils
Installed Packages
Name : binutils
Arch : x86_64
Version : 2.23.52.0.1
Release : 55.el7
Size : 20 M
Repo : installed
From repo : anaconda
Summary : A GNU collection of binary utilities
URL : http://sources.redhat.com/binutils
#
</pre>
</div>
<div>
<br />
<h4>
4.2.2 Version of C++ compiler</h4>
</div>
<div>
<div>
We installed the following version of the C++ compiler:<br />
<br /></div>
</div>
<pre class="brush: java">#
$ sudo yum install gcc-c++
...
$yum info gcc-c++
Installed Packages
Name : gcc-c++
Arch : x86_64
Version : 4.8.5
Release : 4.el7
Size : 16 M
Repo : installed
From repo : base
Summary : C++ support for GCC
URL : http://gcc.gnu.org
#</pre>
Version “4.8.5” works perfectly.
<br />
<h3>
4.3 Install and configure OracleXE </h3>
<div>
When using CentOS we can follow installation instructions straight from the book.
The instructions to follow can be found <a href="https://github.com/oracle/node-oracledb/blob/master/INSTALL.md" target="_blank">here</a>.
Because we plan to have our development project in the same box as the database, we choose option 4: <a href="https://github.com/oracle/node-oracledb/blob/master/INSTALL.md#instoh" target="_blank">Node-oracledb Installation on Linux with a Local Database</a>.
<br />
Anyway, it can be done like this:<br />
Within your Host OS (Win7 for me), switch into the root directory of the engineering project and create a directory for downloading the software we need.
Switch into the project’s root directory, and create a “downloads” directory. Visit the theOracle Download page and download the Oracle XE 11g for Linux into the “downloads” directory.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://3.bp.blogspot.com/-Z6K0SXFNcRU/VsGyq2FqOwI/AAAAAAAAAMg/oIAJhL6w_kI/s1600/oracle.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="185" src="https://3.bp.blogspot.com/-Z6K0SXFNcRU/VsGyq2FqOwI/AAAAAAAAAMg/oIAJhL6w_kI/s320/oracle.JPG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Oracle Download Page</td></tr>
</tbody></table>
The nice thing about Vagrant is that it automatically shares your project root directory (the directory where your Vagrantfile lies) and all sub-directories, between your Host and your Guest-OS.
You can now ssh into your box (if it is not running, start it up by typing “vagrant up”) and change into the project root. Vagrant shares this under the name /vagrant.<br />
The Oracle XE zip-file is now in here: “<PROJECT_ROOT>\downloads”.
From here on, we will proceed from within your new box.
Switch into your project's root directory.
If you have not already done so, power up the box, ssh into it and proceed as follows:
</div>
<div>
</div>
<pre class="brush: java">#
# cd <eng_root>
vagrant up
vagrant ssh
#
# now we are within the box
sudo yum install libaio bc flex
sudo yum install unzip
#
# switch into the directory where you put the oracle zip
cd /vagrant/downloads
#
# unzip (make sure to name of the zip-file is correct)
sudo unzip -q oracle-xe-11.2.0-1.0.x86_64.rpm.zip
#
# switch into the newly created Disk1 directory and install the package
cd Disk1/
sudo rpm -ivh oracle-xe-11.2.0-1.0.x86_64.rpm
#</pre>
<div>
<br />
Good. The database is installed, but has to be configured now.<br />
The following has to be done as root (password for root-account in vagrant boxes should be “vagrant”):<br />
<br />
<pre class="brush: java">#
# password for root should be: vagrant
su –
#
# now call Oracle’s configure script:
/etc/init.d/oracle-xe configure
#
# it will ask you some questions.
# I accepted all the defaults, which are these:
# http Apex port: 8080
# Database Listener Port: 1521
# a password for sys and system account: think of something…
# start OracleDB on startup: yes
#
# When ready, you should see this message:
Starting Oracle Net Listener...Done
Configuring database...Done
Starting Oracle Database 11g Express Edition instance...Done
Installation completed successfully.
#
</pre>
<br />
You now have a box with Oracle XE installed, configured, up and running.<br />
What we need to do now is to set and export a couple of environment variables.<br />
I choose to put these calls into my “~/.bash_profile” file (place it wherever you like, as long as it is executed when you enter the box).<br />
<br />
This is how my “~/.bash_profile” looks after editing (you do not need to be root any longer by the way):<br />
# .bash_profile<br />
<br />
<pre class="brush: java">#
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
. /u01/app/oracle/product/11.2.0/xe/bin/oracle_env.sh
PATH=$PATH:$HOME/bin
LD_LIBRARY_PATH=$ORACLE_HOME/lib
export LD_LIBRARY_PATH
export PATH
#</pre>
<br />
What is this for:<br />
<ul>
<li>First we have to call an Oracle script that sets and exports some environment variables</li>
<li>Next we have to define and export an environment variable that tells Oracle clients where the shared libraries are located, which are needed to connect to the database</li>
</ul>
<br />
You are now able to access your database from within the box, say, via sqlplus. Just ssh into the box and start sqlplus (use the credentials you specified during database configuration):<br />
<br />
<pre class="brush: java">#
Last login: Sat Feb 13 11:27:02 2016 from 10.0.2.2
[vagrant@localhost ~]$ sqlplus system/system
SQL*Plus: Release 11.2.0.2.0 Production on Sat Feb 13 19:41:35 2016
Copyright (c) 1982, 2011, Oracle. All rights reserved.
Connected to:
Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
SQL> desc user_tables
Name Null? Type
----------------------------------------- -------- ----------------------------
TABLE_NAME NOT NULL VARCHAR2(30)
TABLESPACE_NAME VARCHAR2(30)
CLUSTER_NAME VARCHAR2(30)
IOT_NAME VARCHAR2(30)
STATUS VARCHAR2(8)
PCT_FREE NUMBER
...
</pre>
You want to access the database inside of your box from outside the box, i.e. from the guest OS (Windows in my case)?
All you have to do is to export some ports to the outside of your box. You do this in your Vagrantfile.
My Vagrantfile looks like this:
<br />
<br />
<pre class="brush: java">#
[vagrant@localhost vagrant]$ cat vagrantfile
Vagrant.configure(2) do |config|
config.vm.box = "geerlingguy/centos7"
config.vm.network :forwarded_port, guest: 1521, host: 1523, host_ip: "127.0.0.1"
config.vm.network :forwarded_port, guest: 8080, host: 8888, host_ip: "127.0.0.1"
end</pre>
<br />
I exposed port 1521 as 1523 for the database and port 8080 as 8888 for Apex.
When connecting to the database from Windows, my host OS, these are the ports I have to specify for the connection.
Here is an example connecting SqlDeveloper:
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://4.bp.blogspot.com/-G6uSQo05OFQ/VsGyzKOA1fI/AAAAAAAAANA/mNHk6KJI1W4/s1600/sqldev.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="173" src="https://4.bp.blogspot.com/-G6uSQo05OFQ/VsGyzKOA1fI/AAAAAAAAANA/mNHk6KJI1W4/s320/sqldev.JPG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Connecting from outside of the box</td></tr>
</tbody></table>
Remember to specify the exported port to access the database.
</div>
<div>
<br /></div>
<div>
That concludes the Oracle XE part. Next, let’s add NodeJS to the box.<br />
<h3>
4.4 Choose a decent NodeJS Release</h3>
Installing NodeJS will be the final task in our engineering project. When this is done, the box is ready to be used in a little development project.<br />
<br />
What is important when going for a NodeJS distribution?<br />
<br />
When trying to install the oracledb driver into a NodeJS project, we faced this problem:<br />
<blockquote class="tr_bq">
erreur: ‘REPLACE_INVALID_UTF8’ is not a member of ‘v8::String’<br />
static const unsigned kReplaceInvalidUtf8 = v8::String::REPLACE_INVALID_UTF8;</blockquote>
Some Google research suggested that the V8 engine received a patch recently which led to a new entry for the file “v8.h”, namely ‘REPLACE_INVALID_UTF8’.<br />
It turned out that some NodeJS distributions do not jet have been patched.<br />
To check yours, find the v8.h file in your installation and check for the mentioned property.<br />
<br />
We downloaded the NodeJS binary tarball directly from the <a href="https://nodejs.org/en/download/" target="_blank">NodeJS download page</a> and saved it into our “downloads” directory. Then we just followed the install instructions, which go like this:<br />
<br />
<pre class="brush: java">#
# copy NodeJS binaries to /opt kopieren and untar it
# again, do this as root...
su -
cp node-v4.2.4-linux-x64.tar.gz /opt
cd /opt
tar -zxf node-v4.2.4-linux-x64.tar.gz
#
</pre>
<br />
Exit the root shell and do the rest as the regular vagrant user.<br />
The path to NodeJS has to be appended to the PATH variable.<br />
Here is the contents of my .bash_profile file afterwards:<br />
<pre class="brush: java">#
[vagrant@localhost ~]$ cat .bash_profile
# .bash_profile
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
# User specific environment and startup programs
. /u01/app/oracle/product/11.2.0/xe/bin/oracle_env.sh
PATH=/opt/node-v4.2.4-linux-x64/bin:$PATH:$HOME/.local/bin:$HOME/bin
LD_LIBRARY_PATH=$ORACLE_HOME/lib
export LD_LIBRARY_PATH
export PATH
</pre>
(This may be different for you, depending on the NodeJS version you installed)<br />
<br />
To test you NodeJS installation, exit and reenter the shell and ask for the versions of node and npm:<br />
<br />
<pre class="brush: java">#
[vagrant@localhost ~]$ node -v
v4.2.4
[vagrant@localhost ~]$ npm -v
2.14.12
#</pre>
…looks good.<br />
<h3>
4.5<span style="white-space: pre;"> </span>Package the Box</h3>
Our VM is now ready.<br />
To easily make use of it in our development projects, we will package it as a new Vagrant Box to be stored locally for further use.<br />
<br />
From within the <eng_root> directory, do this:<br />
<br />
<pre class="brush: java">#
# package the box:
vagrant package --output centos7_oraclexe_node_01.box
#
# Add the box to the Vagrant System:
vagrant box add centos7_oraclexe_node_01 centos7_oraclexe_node_01.box
#
# delete the box-file
rm centos7_oraclexe_node.box
#</pre>
<br />
The new Vagrant box is now part of your Vagrant environment:<br />
<br />
<pre class="brush: java">#
$ vagrant box list
centos7_oraclexe_node_01 (virtualbox, 0)
geerlingguy/centos7 (virtualbox, 1.0.8)
#
</pre>
As you can see, our new box is listed together with the geerlingguy box which we used as a base box.<br />
<br />
We are done with the engineering project.<br />
The new box can now be utilized in our development projects.<br />
<h2>
5<span style="white-space: pre;"> </span>Development Task</h2>
In order to test your new box let’s create a little development project.<br />
<h3>
5.1<span style="white-space: pre;"> </span>Set up the Vagrant Box</h3>
Create a project directory, which we will call <dev_root> from now on.<br />
From within this directory, let Vagrant create a Vagrantfile using the new box from the engineering project:<br />
<br />
<pre class="brush: java">#
# create the vagrantfile within your <dev_root> directory
vagrant init –m centos7_oraclexe_node_01
#</pre>
Afterwards, edit the Vagrantfile so that it looks somewhat like this:<br />
<pre class="brush: java">#
Vagrant.configure(2) do |config|
config.vm.box = "centos7_oraclexe_node_01"
config.vm.provider "virtualbox" do |v|
v.name = "mybox"
end
config.vm.network :forwarded_port, guest: 1521, host: 1523, host_ip: "127.0.0.1"
config.vm.network :forwarded_port, guest: 8080, host: 8888, host_ip: "127.0.0.1"
config.vm.network :forwarded_port, guest: 5000, host: 3000, host_ip: "127.0.0.1"
end
#</pre>
<br />
The last port forwarding is for the NodeJS http server we will create later. It will be listening von port 5000 in the Guest OS and reachable on port 3000 from the Host OS.<br />
Start up your new box like this:<br />
<pre class="brush: java">#
vagrant up
</pre>
<br /></div>
<div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://3.bp.blogspot.com/-VcWmfBu7ug8/VsGy0DY6JDI/AAAAAAAAANA/cTTYKf65tvs/s1600/vagrant_up.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="154" src="https://3.bp.blogspot.com/-VcWmfBu7ug8/VsGy0DY6JDI/AAAAAAAAANA/cTTYKf65tvs/s320/vagrant_up.JPG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Starting up the box for development</td></tr>
</tbody></table>
</div>
<div>
Two remarks here:<br />
<ul>
<li>This screenshot was done after a vagrant reload; your “vagrant up” should produce a similar output</li>
<li>I installed the vagrant-vbguest plugin which synchronizes VirtualBox Guest Additions between Host and Guest system (without this plugin you may get some additional warnings)</li>
</ul>
<br />
Anyway, your box is up and running. Oracle XE is listening to you and NodeJS is ready to take your commands.<br />
Let’s set up a NodeJS project.<br />
<h3>
5.2<span style="white-space: pre;"> </span>Create a NodeJS Project</h3>
Follow your favorite path to initializing a NodeJS project.<br />
This example will start with “npm init” from inside the box:<br />
<pre class="brush: java">#
# ssh into your box
vagrant ssh
#
# switch into the projects root directory <dev_root>
cd /vagrant
npm init
#
# answer the questions and get a package.json file in return
#
# install express
npm install --no-bin-links express --save
</pre>
<br />
<b>Attention</b><br />
Did you notice the “--no-bin-links” option?<br />
You have to specify this option when installing any NodeJS module which tries to create SymLinks to binaries within your box (and most modules do…). There seems to be a permission problem between Vagrant/VirtualBox and Windows regarding these SymLinks.<br />
The Npm team recognized the problem and provided the option shown above.<br />
<h3>
5.3<span style="white-space: pre;"> </span>Install oracledb NodeJS Database Driver</h3>
The oracledb NodeJS driver is maintained on Github <a href="https://github.com/oracle/node-oracledb" target="_blank">here</a>.<br />
The project is maintained by <a href="mailto:christopher.jones@oracle.com">christopher.jones@oracle.com</a>. It contains everything you need to get started, like installation instructions, examples and API reference.<br />
<br />
Now comes the moment of truth, when we install the oracledb driver:<br />
<br />
<pre class="brush: java">#
# still inside the box in the /vagrant directory:
# do “npm install oracledb”
[vagrant@localhost ~]$ npm install oracledb
|
> oracledb@1.5.0 install /home/vagrant/node_modules/oracledb
> node-gyp rebuild
make: Entering directory `/home/vagrant/node_modules/oracledb/build'
CXX(target) Release/obj.target/oracledb/src/njs/src/njsOracle.o
CXX(target) Release/obj.target/oracledb/src/njs/src/njsPool.o
CXX(target) Release/obj.target/oracledb/src/njs/src/njsConnection.o
CXX(target) Release/obj.target/oracledb/src/njs/src/njsResultSet.o
CXX(target) Release/obj.target/oracledb/src/njs/src/njsMessages.o
CXX(target) Release/obj.target/oracledb/src/njs/src/njsIntLob.o
CXX(target) Release/obj.target/oracledb/src/dpi/src/dpiEnv.o
CXX(target) Release/obj.target/oracledb/src/dpi/src/dpiEnvImpl.o
CXX(target) Release/obj.target/oracledb/src/dpi/src/dpiException.o
CXX(target) Release/obj.target/oracledb/src/dpi/src/dpiExceptionImpl.o
CXX(target) Release/obj.target/oracledb/src/dpi/src/dpiConnImpl.o
CXX(target) Release/obj.target/oracledb/src/dpi/src/dpiDateTimeArrayImpl.o
CXX(target) Release/obj.target/oracledb/src/dpi/src/dpiPoolImpl.o
CXX(target) Release/obj.target/oracledb/src/dpi/src/dpiStmtImpl.o
CXX(target) Release/obj.target/oracledb/src/dpi/src/dpiUtils.o
CXX(target) Release/obj.target/oracledb/src/dpi/src/dpiLob.o
CXX(target) Release/obj.target/oracledb/src/dpi/src/dpiCommon.o
SOLINK_MODULE(target) Release/obj.target/oracledb.node
COPY Release/oracledb.node
make: Leaving directory `/home/vagrant/node_modules/oracledb/build'
oracledb@1.5.0 node_modules/oracledb
+-- nan@2.1.0
[vagrant@localhost ~]$
</pre>
<br />
If you see an output like this, without any errors, then it’s done.<br />
<br />
<h3>
5.4<span style="white-space: pre;"> </span>Test your installation</h3>
Let’s code a minimal web service to test the installation.<br />
I do this within my Windows host using Webstorm.<br />
Here is a small test server coded in a single file “./app/server.js”:<br />
<pre class="brush: java">
/**
* Created by vkoster on 14.02.2016.
* Testing my oracledb installation with a little web-service.
*/
var express = require('express');
var app = express();
var oracledb = require('oracledb');
oracledb.outFormat = oracledb.OBJECT;
// listen on port 5000, which is forwarded to 3000 in the Host OS
var port = Number(process.env.port || 5000);
/**
* url: /test
* Method: get
* Description: Select all tables owned by the connected user
*/
app.get('/test', function getClients(req, res){
oracledb.getConnection(
// param 1: credentials
{
user : "system",
password : "system",
connectString : "localhost/xe"
},
// param 2: callback to be called with a connection
function getTableList(err, connection) {
if (err) {
console.error(err.message);
res.status(400).send({"status ": 400,
"error": "Message: "+err.message});
return;
}
// we are connected...
console.log("we are connected...");
connection.execute(
// Param 1: statement
"SELECT table_name " +
"FROM user_tables",
// Param 2: callback to be called with result
function(err, result) {
if (err) {
console.error(err.message);
connection.release(
function(err) {
if (err) {
console.error(err.message);
res.status(400).send({"status": 400,
"error": "Message: "+err.message});
}
});
return;
}
connection.release(
function(err) {
if (err) {
console.error(err.message);
res.status(400).send({"status": 400,
"error": "Message: "+err.message});
return;
}
console.log("connection released...")
});
console.log("we have a result");
res.status(200).send(JSON.stringify(result));
});
} // getTableList
); // getConnection
});
// Handle everything else
app.use(function(req, res){
console.log('route not handled');
res.status(404).send({"status": 404,
"error": "route not handled"
});
});
// App start...
app.listen(port, function nowListening(){
console.log('Listening on port: '+port);
});
</pre>
<br />
"ssh" into your box, switch into the “/vagrant” directory and start the application:<br />
<br />
<pre class="brush: java">#
# after “vagrant ssh”:
[vagrant@localhost vagrant]$ cd /vagrant
[vagrant@localhost vagrant]$ node ./app/server.js
Listening on port: 5000
</pre>
Remember that the server listens on port 5000 within your box, but this port is forwarded to port 3000 of the Host OS.<br />
Therefor point your browser to this url: <a href="http://localhost:3000/test">http://localhost:3000/test</a><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://4.bp.blogspot.com/-clQUudosNvU/VsGyz4CwpxI/AAAAAAAAANA/6cN-o3NAu-U/s1600/Browser.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="120" src="https://4.bp.blogspot.com/-clQUudosNvU/VsGyz4CwpxI/AAAAAAAAANA/6cN-o3NAu-U/s320/Browser.JPG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Access NodeJS via Browser</td></tr>
</tbody></table>
(user “system” owns quite some tables ?)<br />
… or do the following curl:<br />
<pre class="brush: java">#
curl -X GET http://localhost:3000/test
</pre>
<br />
Result for curl:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://2.bp.blogspot.com/-SOomEPZ8vZI/VsGyzXT8oSI/AAAAAAAAANA/qnt1hOq3WzQ/s1600/Curl.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="142" src="https://2.bp.blogspot.com/-SOomEPZ8vZI/VsGyzXT8oSI/AAAAAAAAANA/qnt1hOq3WzQ/s320/Curl.JPG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Access NodeJS via curl</td></tr>
</tbody></table>
Server terminal:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://4.bp.blogspot.com/-x3TD8-HF5Kg/VsGyzlwAHnI/AAAAAAAAANA/3f3FJh9CuHA/s1600/terminal.JPG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="67" src="https://4.bp.blogspot.com/-x3TD8-HF5Kg/VsGyzlwAHnI/AAAAAAAAANA/3f3FJh9CuHA/s320/terminal.JPG" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">NodeJS Terminal</td></tr>
</tbody></table>
It’s done.<br />
We have a box containing OracleXE and NodeJS.<br />
We can use this box in our development projects writing NodeJS apps against OracleXE.<br />
<h2>
6<span style="white-space: pre;"> </span>One unresolved Problem</h2>
I cannot spare you one issue I was not able to resolve.<br />
On some occasions, the Oracle TNS listener running inside the box cannot be reached from my Host OS.<br />
The strange thing is that it is running. I can always connect to the database using Sqlplus from inside of the box.<br />
But the database cannot be reached from the outside (Host OS).<br />
The really strange thing here is that while this could be a NAT-ing problem, even the oracledb driver, which also resides inside the box, cannot reach the database.<br />
<br />
I’m using two workarounds to overcome this:<br />
•<span class="Apple-tab-span" style="white-space: pre;"> </span>Reloading the box while being disconnected from the network<br />
•<span class="Apple-tab-span" style="white-space: pre;"> </span>Using password authentication in the Vagrantfile (makes Vagrant insert new ssh key pairs into the Guest OS)<br />
<br />
It would be great if anyone could shed some light on this problem.<br />
Please send me a comment!<br />
<h2>
7<span style="white-space: pre;"> </span>Was it worth it?</h2>
There is no alternative to using oracledb NodeJS driver if you want to access an Oracle database from within NodeJS. If you are in Windows, installing directly on your Host OS may not be the best of ideas.<br />
<br />
Using Vagrant we now have a stable and very reproducible environment that can be easily shared with others across different host systems.<br />
This alone would be reason enough for me to keep on working like this.<br />
<br />
By having Vagrant expose the database to the host machine, we can use already installed tooling like e.g. SqlDeveloper by just configuring a new connection. If you are a SqlPlus guy you can of course use this from within the VM. It’s up to you.<br />
As Vagrant automatically shares your project’s root directory between the Window’s host and the VM, you can use your favorite IDE to edit your files and then run them on Node from within the VM.<br />
<br />
Ok, there is this SymLink issue and the occasional problem with Oracle's tns-listner (guess I have to dig deeper with this one).<br />
But apart from this, I’m a happy developer. I learned to love Vagrant boxes use them all around by now.<br />
<h2>
8<span style="white-space: pre;"> </span>Summery</h2>
Here’s a short list of things to look out for:<br />
<br />
<ul>
<li>Go for a CentOS 64 Bit Base Image</li>
<li>See that binutils is of version <b>2.23.52.0.1</b> or higher</li>
<li>See that your Compiler is of version <b>4.8.5</b> or higher</li>
<li>Check your NodeJS installation for the property "REPLACE_INVALID_UTF8" in v8.h</li>
<li>Follow the oracledb installation instructions closely, especially regarding environment variables</li>
<li>Install additional NodeJS modules with the <b>–no-bin-links</b> option</li>
</ul>
Have fun.<br />
<h2>
9<span style="white-space: pre;"> </span>References</h2>
<ul>
<li>"Pro Vagrant" Book</li>
<li>The oracledb Github project</li>
<li>And too many blogs and Stackoverflow entries to mention here, every one of them much appreciated and highly regarded.</li>
</ul>
<br />
<br /></div>
Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com1tag:blogger.com,1999:blog-1625776533566626066.post-32393568942579308202015-09-28T23:07:00.002+02:002016-02-15T11:53:54.853+01:00Couchdb, MVCC and Conflicts while Replicating<h2>
Setup</h2>
In my <a href="http://jeeonthepi.blogspot.de/2015/02/couchdb-mvcc-and-conflicts.html" target="_blank">last post</a> regarding Multiversion Concurrency Control, we saw what it takes to enter conflicting versions of a document into a single couch instance. You have to be somewhat resourceful.<br />
<br />
But the real fun with the couch comes from its distributed nature.<br />
We will see that the rules change a bit when we talk about more than one instance and use replication to synchronize them.<br />
<br />
Here's the setup for playing around with MVCC on two couches:<br />
<br />
First Pi:<br />
<br />
<ul>
<li>hostname: frodo</li>
<li>Model: Pi B+ (ARMv6h)</li>
<li>OS: Arch Linux ARM</li>
<li>Couchdb 1.6.1_4 (taken from Arch Linux ARM Repository)</li>
</ul>
<div>
<br />
Second Pi:</div>
<div>
</div>
<br />
<ul>
<li>hostname: arwen</li>
<li>Model: Pi Model 2 (ARMv7h)</li>
<li>OS: Raspbian</li>
<li>Couchdb 1.6.1 (<a href="http://jeeonthepi.blogspot.de/2015/09/installing-couchdb-161-on-raspberry-pi.html" target="_blank">compiled on this Pi</a>)</li>
</ul>
<div>
<br /></div>
<div>
Again, everything will be done via curl. Data will not be directly on the command line but always be taken from a file (due to strange behaviour of my curl on Windows).</div>
<div>
<br /></div>
<div>
We assume two users entering data into the their respective Pis. Arwen uses the the couch on her arwen-Pi when Frodo uses his frodo-Pi. Eventually they will exchange their work via replication.</div>
<h2>
Preparation</h2>
<div>
Let's start from scratch by creating the database on each Pi respectively:</div>
<pre class="brush: java">#
# create the database on arwen
curl -X PUT http://arwen:5984/mvcc
#
# reaponse
{"ok":true}
#
# create the database on frodo
curl -X PUT http://frodo:5984/mvcc
#
# resonse
{"ok":true}
#
</pre>
<div>
<h2>
...And Go</h2>
</div>
<div>
Arwen inserts her document first:<br />
<pre class="brush: java">#
# Arwen inserts her Doc rep_mydoc_u1_1.json
{
"content": "U1_1"
}
#
curl -H "Content-Type: application/json" -d @rep_mydoc_u1_1.json -X PUT http://arwen:5984/mvcc/mydoc
#response
{
"ok":true,
"id":"mydoc",
"rev":"1-3557461c60a30b0d156f8b36a1bdcf9f"
}
#</pre>
<br /></div>
<div>
Arwen wants to share her document with Frodo. She submits a <b>Push Replication Request</b> into the <b>_replicator</b> database of her Pi to trigger the replication:<br />
<pre class="brush: java">#
# Arwen shares her doc with frodo via replication
# She initiates a push replication from arwen to frodo
# push_a2f_01.json:
{
"source": "mvcc",
"target": "http://frodo:5984/mvcc"
}
#
curl -H "Content-Type: application/json" -d @push_a2f_01.json -X PUT http://arwen:5984/_replicator/a2s01
#
#responsse
{
"ok":true,
"id":"a2s01",
"rev":"1-0088a4a381404b513bf0586d08d6ce80"
}
#
</pre>
Taking a look into Arwen's couch.log tells us that the replication took place:<br />
<pre class="brush: java">#
Document `a2s01` triggered replication `6018cd9109568fed438add0722e9bccb`
starting new replication `6018cd9109568fed438add0722e9bccb` at <0 data-blogger-escaped-.31751.2=""> (`mvcc` -> `http://frodo:5984/mvcc/`)
recording a checkpoint for `mvcc` -> `http://frodo:5984/mvcc/` at source update_seq 1
Replication `6018cd9109568fed438add0722e9bccb` finished (triggered by document `a2s01`)
#
<!--0--></pre>
<br />
Please note that these Pis do not know about each other. The replication request is the only point of contact. This request requires Arwen to know about a frodo-Pi.<br />
<br />
OK, Frodo should have Arwen's document on his Pi now:<br />
<pre class="brush: java">#
# Frodo should now have the document too:
curl http://frodo:5984/mvcc/mydoc
#respopnse
{
"_id":"mydoc",
"_rev":"1-3557461c60a30b0d156f8b36a1bdcf9f",
"content":"U1_1"
}
#
</pre>
<br /></div>
<div>
Now Arwen and Frodo both continue to work on their respective copy of the document and eventually save their work:</div>
<pre class="brush: java">#
# Arwen edits her document on arwen
# rep_mydoc_u1_2.json:
{
"_rev": "1-3557461c60a30b0d156f8b36a1bdcf9f",
"content": "U1_2"
}
#
curl -H "Content-Type: application/json" -d @rep_mydoc_u1_2.json -X PUT http://arwen:5984/mvcc/mydoc
# response
{
"ok":true,
"id":"mydoc",
"rev":"2-2686fb85c0681a3d8c411617f048f94f"
}
#
# Frodo does the same on frodo
# rep_mydoc_u2_2.json:
{
"_rev":"1-3557461c60a30b0d156f8b36a1bdcf9f",
"content": "U2_2"
}
#
curl -H "Content-Type: application/json" -d @rep_mydoc_u2_2.json -X PUT http://frodo:5984/mvcc/mydoc
# response
{
"ok":true,
"id":"mydoc",
"rev":"2-03b64efa2cd6619f46bcbe618fa791f9"
}
#
</pre>
<div>
Each Pi now holds a different version of the document.<br />
Frodo initiates a full sync by triggering first a push replication followed by a pull replication to Arwen. As Frodo now takes the lead, both requests will be submitted into the_replicator DB of his Pi:<br />
<pre class="brush: java"># Frodo pushes his stuff to Arwen
# push request push_f2a_01.json:
{
"source": "mvcc",
"target": "http://arwen:5984/mvcc"
}
#
curl -H "Content-Type: application/json" -d @push_f2a_01.json -X PUT http://frodo:5984/_replicator/f2a01
#response
{
"ok":true,
"id":"f2a01",
"rev":"1-d20099b5d5b65eb05271be0204d8100a"
}
#
# Next Frodo pulls from Arwen
# pull request pull_a2f_01.json:
{
"source": "http://arwen:5984/mvcc",
"target": "mvcc"
}
curl -H "Content-Type: application/json" -d @pull_a2f_01.json -X PUT http://frodo:5984/_replicator/a2f01
#response
{
"ok":true,
"id":"a2f01",
"rev":"1-26926753f759498b86ece4e48fdb0e5f"
}
#</pre>
</div>
<div>
What would be our expectation after syncing both Pis?<br />
The same document was edited on different hosts. After the new versions had been submitted, each host then held the old and a new version of the document. Both hosts may claim to hold the current version of the document with equal rights.<br />
After a full sync, we expect this:<br />
<br />
<ul>
<li>there is identical data on both hosts</li>
<li>each host holds the old version and both "new" versions of the document</li>
</ul>
<div>
So, let's see.</div>
<div>
We're going to check by requesting the current version and conflicting versions if any.</div>
<div>
Let's check on Arwen first:</div>
<pre class="brush: java">#
# there should be a conflict on arwen now...
curl http://arwen:5984/mvcc/mydoc?conflicts=true
#response
{
"_id":"mydoc",
"_rev":"2-2686fb85c0681a3d8c411617f048f94f",
"content":"U1_2",
"_conflicts":["2-03b64efa2cd6619f46bcbe618fa791f9"]
}
#
</pre>
<div>
The current version is the one that Arwen herself submitted.<br />
As expected, there is a conflict.<br />
<br />
What is it on Frodo's Pi?<br />
<pre class="brush: java">#
# there should be a conflict on frodo too...
curl http://frodo:5984/mvcc/mydoc?conflicts=true
#response
{
"_id":"mydoc",
"_rev":"2-2686fb85c0681a3d8c411617f048f94f",
"content":"U1_2",
"_conflicts":["2-03b64efa2cd6619f46bcbe618fa791f9"]
}
#
</pre>
On Frodo's Pi we find the same situation. Arwen's document is delivered as current. Frodo's version constitutes the conflict.<br />
The couch keeps its promise to deliver the same "winning" version on both nodes.<br />
<h2>
Summery</h2>
<div>
As far as conflicts are concerned, working distributed changes the rules completely.<br />
On a single node, the couch is quite strict avoiding conflicts. You need a bulk update with a special mode switched on to get it done.<br />
Once you decide to work distributed, the priorities change. When replicating between nodes, pushing or pulling your data successfully becomes the main objective. The goal is to save data over a network. As the nodes operate completely independent from one another, conflicts cannot be avoided.<br />
<br />
Well, if you need to go for distributed and want your nodes to be independent, this is the price you have to pay. As economics teaches us: there is no such thing as a free lunch. This seems to hold true for the computer scientist's menu too.</div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
</div>
<div>
<br /></div>
Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com0tag:blogger.com,1999:blog-1625776533566626066.post-42023081501375166562015-09-20T19:33:00.001+02:002015-11-03T08:01:12.213+01:00Installing Couchdb 1.6.1 on a Raspberry Pi Model 2<h2>
Couchdb 1.6.1 on a Pi 2</h2>
<div>
<h3>
Update 03.11.2015</h3>
<div>
The Erlang Solutions Repository now contains a new version of Erlang. The major version is now 18. This is too high for the couch in version 1.6.x.</div>
<div>
For this reason, please omit the step of including the Erlang Solutions Repository.</div>
<div>
Just rely on what you get from the default Raspbina/Debian repos.</div>
<div>
I still have to verify this with Wheezy, but for the new Raspbian Jessie image this does the trick.<br />
</div>
<h3>
Installing the couch version 1.6.1</h3>
This will probably be my shortest post ever.</div>
<div>
Last week I installed couchdb version 1.6.1 on my Raspberry Pi Model 2.</div>
<div>
I did this for two reasons. One was to have the couch on Pi 2. The second was to see if my own <a href="http://jeeonthepi.blogspot.de/2014/08/installing-couchdb-1.html" target="_blank">install instructions</a> are still valid for couch 1.6.1 and Pi Model 2. Two readers reported problems, so I was a little worried.</div>
<div>
<br /></div>
<div>
But everything worked well and it was soon time to relax.</div>
<div>
I went along the instructions using copy and past, with only two exceptions:</div>
<div>
<ul>
<li>new Raspbian image: 2015-05-05-raspbian-wheezy.img</li>
<li>new couch code: <span style="background-color: white; box-sizing: border-box; color: #585ac2; font-family: "droid serif" , "georgia" , "times new roman" , "times" , serif; font-size: 14px; font-weight: 700; line-height: 20px; text-decoration: none;"><a href="http://ftp-stud.hs-esslingen.de/pub/Mirrors/ftp.apache.org/dist/couchdb/source/1.6.1/apache-couchdb-1.6.1.tar.gz" style="background-color: white; box-sizing: border-box; color: #585ac2; font-family: 'Droid Serif', Georgia, 'Times New Roman', Times, serif; font-size: 14px; line-height: 20px; text-decoration: none;">http://ftp-stud.hs-esslingen.de/pub/Mirrors/ftp.apache.org/dist/couchdb/source/1.6.1/apache-couchdb-1.6.1.tar.gz</a></span></li>
</ul>
<div>
The instructions are still valid. They worked for me and should do so for you.</div>
</div>
<div>
<br /></div>
<div>
Have fun.</div>
<div>
<br /></div>
Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com2tag:blogger.com,1999:blog-1625776533566626066.post-49146664725762555222015-02-10T23:14:00.000+01:002015-09-28T23:09:22.215+02:00CouchDB - MVCC and ConflictsThis is a small entry about couchDB's Multi Version Concurrency Control mechanism and what it takes to have conflicting documents end up on the couch.<br />
Though MVCC is well covered by couchDB's documentation, I wanted to see it in action with my own Pies :-)<br />
<h2>
Setup</h2>
I have couchDB installed on two Pies, gandalf and samwise. On gandalf, the couchdb version is 1.6.0 whereas on samwise it is a 1.5.1.<br />
<br />
We will first create conflicts on a single node (gandalf) and then on two nodes by means of master-master replication.<br />
Curl will be used to talk to the couches (note: my curl shell on Windows does not like mixing " and ' which is why I have to put all the JSON data I want to send via curl into files).<br />
<br />
If you want to replay this on your system, make sure to not only adjust IP addresses or host names but also substitute the revision values (_rev) with the ones you'll receive as response.<br />
All curl commands and there respective responses are genuine. Responses are formatted for better readability.<br />
<h2>
What is a Conflict?</h2>
Before we start, let's agree on what a conflict is:<br />
<span style="background-color: #666666;">A conflict is a state where two or more versions of a document branch from a common root version. Only the leafs of conflicting branches are considered to be in conflict with each other.</span><br />
Let's try to create this on couchdb.<br />
<br />
We'll be acting on behalf of two users, first on one and then on two couchdb nodes.<br />
<h2>
Conflicts on a Single Node</h2>
On a single instance of couchdb, it is not possible to create a conflict when performing single document updates. If you want to update a document, you have to supply the latest revision of this document's revision tree. If you do not have this revision, your update will be rejected.<br />
<br />
If you want to end up with a conflict, i.e. two revisions branching from a single common revision, you have to use couchdb's bulk update feature. But that's not all it takes. In addition you have to use the bulk update in the special "All-or-Nothing" mode.<br />
<br />
Not so easy to to create a conflict on a single instance, but lets see.<br />
<br />
We start by creating a database called <span style="background-color: #666666;">mvcc</span> on gandalf:<br />
<pre class="brush: java"># check if couchdb is running
curl http://gandalf:5984
# response:
{"couchdb":"Welcome",
"uuid":"360325151b6a3c70595a522b36f52037",
"version":"1.6.0",
"vendor":{"name":"The Apache Software Foundation",
"version":"1.6.0"}
}
#
# create database "mvcc"
curl -X PUT http://gandalf:5984/mvcc
# response:
{"ok":true}
</pre>
<br />
User 1 inserts an initial version of a document into the database.
The document is stored in file <span style="background-color: #666666;">mydoc_u1_1.json</span> and looks like this:
<br />
<pre class="brush: java">{
"content": "U1_1"
}
</pre>
<br />
<pre class="brush: java">curl -H "Content-Type: application/json" -d @mydoc_u1_1.json -X PUT http://gandalf:5984/mvcc/mydoc
# response:
{"ok":true,
"id":"mydoc",
"rev":"1-3557461c60a30b0d156f8b36a1bdcf9f"
}
</pre>
<br />
User 2 reads the document and takes down the revision in order to use it for the update he plans.
<br />
<pre class="brush: java"># User 2 reads the doc...
curl -X GET http://gandalf:5984/mvcc/mydoc
# response:
{"_id":"mydoc",
"_rev":"1-3557461c60a30b0d156f8b36a1bdcf9f",
"content":"U1_1"
}
</pre>
<br />
Both users are now holding the same revision of the document and both plan to update the document.
User 1 is faster and places his update.
<br />
<pre class="brush: java"># here is the updated doc (mydoc_u1_2.json)
{
"_rev":"1-3557461c60a30b0d156f8b36a1bdcf9f",
"content": "U1_2"
}
#
# ...and the update...
curl -H "Content-Type: application/json" -d @mydoc_u1_2.json -X PUT http://gandalf:5984/mvcc/mydoc
# response:
{"ok":true,
"id":"mydoc",
"rev":"2-2686fb85c0681a3d8c411617f048f94f"
}
</pre>
<br />
Done. We hava a second revision of the document.
User 2 will now submit his update, but he still holds revision 1. Here is his update.
<br />
<pre class="brush: java"># here is the document...
{
"_rev":"1-3557461c60a30b0d156f8b36a1bdcf9f",
"content": "U2_1"
}
#
# Note that it indeed references the 1 revision of the document
# Now the update itself:
curl -H "Content-Type: application/json" -d @mydoc_u2_1.json -X PUT http://gandalf:5984/mvcc/mydoc
# response:
{"error":"conflict",
"reason":"Document update conflict."
}
</pre>
<br />
Here we see the expected result: You are not allowed to update a document if you do not have the latest revision. Another way of saying this is, you can only update the latest revision of a document or slightly different again, you cannot branch the document.
At least not in single document update mode.
<br />
User 2 may be a bit slow, but he is resourceful. He knows about couchdb's bulk update interface and that this is a way to fork a branch from revision 1.
So here is what he does:
<br />
<pre class="brush: java"> # this is the bulk doc (bulk_u2_1.json):
{
"docs": [{
"_id": "mydoc",
"_rev":"1-3557461c60a30b0d156f8b36a1bdcf9f",
"content": "U2_1"
}]
}
</pre>
<br />
Granted, this is some sorry bulk file, consisting only of a single document...<br />
<pre class="brush: java"># user 2 tries the bulk interface:
curl -H "Content-Type: application/json" -d @bulk_u2_1.json -X POST http://gandalf:5984/mvcc/_bulk_docs
# response:
[{"id":"mydoc",
"error":"conflict",
"reason":"Document update conflict."
}]
</pre>
<br />
Same result as before. Using the bulk interface is not enough. It has to be used with the all-or-nothing option. This is what user 2 tries next.
<br />
Now the bulk document contains the all_or_noting property.
<br />
<pre class="brush: java"># bulk-doc: bulk_u2_2.json
{
"all_or_nothing": true,
"docs": [{
"_id": "mydoc",
"_rev":"1-3557461c60a30b0d156f8b36a1bdcf9f",
"content": "U2_1"
}]
}
#
# ...and now the update:
curl -H "Content-Type: application/json" -d @bulk_u2_2.json -X POST http://gandalf:5984/mvcc/_bulk_docs
# response:
[{"ok":true,
"id":"mydoc",
"rev":"2-ba85ce56711c69f7d6200935357d79f9"
}]
</pre>
<br />
Success: this time, the update was accepted. We now have one root-revision and two revisions branching from that root revision:
<br />
<pre class="brush: java"># the revision tree:
root: 1-3557461c60a30b0d156f8b36a1bdcf9f
branch 1: 2-2686fb85c0681a3d8c411617f048f94f
branch 2: 2-ba85ce56711c69f7d6200935357d79f9
</pre>
<br />
Now that we finally have a conflict, how does couchdb deal with it?
Let's simply retrieve the document and see what we get.
<br />
<pre class="brush: java"># a simple get...
curl http://gandalf:5984/mvcc/mydoc
# response:
{"_id":"mydoc",
"_rev":"2-ba85ce56711c69f7d6200935357d79f9",
"content":"U2_1"
}
</pre>
<br />
Couchdb determines a "winner" and does not let the conflict surface as long as you do not specifically ask for it.
<br />
Let's ask for it.
<br />
<pre class="brush: java"># fetch current document and all conflicting revisions...
curl http://gandalf:5984/mvcc/mydoc?conflicts=true
# response:
{"_id":"mydoc",
"_rev":"2-ba85ce56711c69f7d6200935357d79f9",
"content":"U2_1","_conflicts":["2-2686fb85c0681a3d8c411617f048f94f"]
}
</pre>
<br />
Couchdb presents the revision inserted by user 2 as the winning revision. The version introduced by user 1 appears in the conflicts list.
<br />
User 1 may not be aware of the fact that his revision is no longer in favor. He continues to update his branch of the document.
<br />
<pre class="brush: java"># user 1 updates his branch of the document (mydoc_u1_3.json)
{
"_rev":"2-2686fb85c0681a3d8c411617f048f94f",
"content": "U1_3"
}
#
# here is the update:
curl -H "Content-Type: application/json" -d @mydoc_u1_3.json -X PUT http://gandalf:5984/mvcc/mydoc
#response
{"ok":true,
"id":"mydoc",
"rev":"3-627f10af94aaf3f31a20c9277c68219a"}
</pre>
<br />
No problem with this update. This means that once a document is branched, each branch can be updated in its own right.
In our case the branch user 1 maintains is now one revision longer than the branch maintained by user 2.
Let's see what this means in terms of conflicting documents and which branch couchdb now elects to be the winner.
<br />
We do a regular GET with the conflicts option enabled.
<br />
<pre class="brush: java"># GET the winning revision and all conflicting revisions:
curl http://gandalf:5984/mvcc/mydoc?conflicts=true
# response:
{"_id":"mydoc",
"_rev":"3-627f10af94aaf3f31a20c9277c68219a",
"content":"U1_3","_conflicts":["2-ba85ce56711c69f7d6200935357d79f9"]
}
</pre>
<br />
We can conclude two things from the result of this GET. One is that the winning branch has changed. The branch of user 1, which has the highest revision number, is now the winner. Another thing to notice is that the conflict moved up the document tree into its leaves.<br />
<h2>
Summary</h2>
Short summary on "Conflicts on a Single Couchdb Instance":
<br />
<br />
<ul>
<li>It's not that easy to produce a conflict on a single instance</li>
<li>Once you have one, you are free to ignore it, couchdb will always decide on a winning revision</li>
<li>In spite of couchdb picking a winner, you are free to follow and work on any branch you please</li>
<li>With every change on any branch, the dice are rolled again an a new winner may turn up</li>
</ul>
<div>
That's it for now on working with a single instance. The <a href="http://jeeonthepi.blogspot.de/2015/09/couchdb-mvcc-and-conflicts-while.html" target="_blank">next entry</a> will deal with two instances (running on two Pies of course :-) and master-master replication between them.</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com0tag:blogger.com,1999:blog-1625776533566626066.post-6010669800465107182014-08-10T14:26:00.000+02:002015-11-11T08:12:23.026+01:00<h2>
Installing CouchDB 1.6.0 on the Raspberry Pi</h2>
<h3>
Update 03.11.2015</h3>
<div>
The Erlang Solutions Repository now contains a new version of Erlang. The major version is now 18. This is too high for the couch in version 1.6.x.</div>
<div>
For this reason, please omit the step of including the Erlang Solutions Repository.</div>
<div>
Just rely on what you get from the default Raspbina/Debian repos.</div>
<div>
I still have to verify this with Wheezy, but for the new Raspbian Jessie image this does the trick.<br />
<br />
Also please note that the couch is now available in version 1.6.1.<br />
The instructions below are valid for this version too, as described <a href="http://jeeonthepi.blogspot.de/2015/09/installing-couchdb-161-on-raspberry-pi.html" target="_blank">here</a>.<br />
<br /></div>
<br />
<h3>
Building CouchDB 1.6.0 On Your Pi</h3>
<div>
CouchDB 1.6.0 has been released and of course we want to have it running on our Pis.<br />
I guess my previous <a href="http://jeeonthepi.blogspot.de/2014/05/installing-couchdb-on-raspberry-pi.html" target="_blank">blog on installing version 1.5.1</a> is still valid but couchDB 1.6.0 is now able to run on Erlang 1.17 and that makes for a sight difference during installation.</div>
<h4>
Preparing the Pi</h4>
<div>
Nothing new here...</div>
<div>
<ul>
<li>I used a brand new 16 GB card.</li>
<li>Download the latest Raspbian Wheezy from <a href="http://www.raspberrypi.org/downloads">www.raspberrypi.org/downloads</a><u><br /></u>At the time of this writing, the image version was 2014-06-20</li>
<li>Install the image on your Pi and do the regular raspi-config</li>
<ul>
<li>Extend the partition</li>
<li>Set your locales</li>
<li>...</li>
<li>Btw, I did not change the default memory split nor did I overclock</li>
</ul>
<li>Re-boot the Pi for the partition extension to take effect</li>
<li>update and upgrade your installation</li>
</ul>
<div>
The Pi is now ready.</div>
</div>
<h4>
Add Erlang Solutions' Repository (omit this step, see update 03.11.2015 hint)</h4>
<div>
Again, we will not add the Cloudant repository for Spidermonkey, but this time add the <a href="https://www.erlang-solutions.com/" target="_blank">Erlang Solutions</a> repository in order to install their Erlang package. This will get you an Erlang 1.17 version which is now ok for couchDB 1.6.0.<br />
The following instructions have been taken from <a href="https://www.erlang-solutions.com/downloads/download-erlang-otp" target="_blank">Erlang Solutions' download</a> section:<br />
<div>
<pre class="brush: java">#
# Add the following line to your /etc/apt/sources.list:
deb http://packages.erlang-solutions.com/debian wheezy contrib
#Next, add the Erlang Solutions public key for apt-secure using following commans:
wget http://packages.erlang-solutions.com/debian/erlang_solutions.asc
sudo apt-key add erlang_solutions.asc
# update repository cache
sudo apt-get update
#
</pre>
</div>
</div>
<div>
<h4>
Install what's needed</h4>
</div>
<div>
Install the following packages:</div>
<div>
<pre class="brush: java">#
# Install Compilers
sudo apt-get install erlang-nox
sudo apt-get install erlang-dev
# Spidermonkey JS engine as lib
sudo apt-get install libmozjs185-1.0
# Development headers for spidermonkey lib
sudo apt-get install libmozjs185-dev
# Dev files for libcurl (openSSL)
sudo apt-get install libcurl4-openssl-dev
# Dev files for icu (Unicode and Locales)
sudo apt-get install libicu-dev
# </pre>
</div>
<div>
<h4>
Create an account for couchDB</h4>
Next we have to create an account for couchDB:<br />
<div>
<pre class="brush: java">#
# Create couchDB account
sudo useradd -d /var/lib/couchdb couchdb
sudo mkdir -p /usr/local/{lib,etc}/couchdb /usr/local/var/{lib,log,run}/couchdb /var/lib/couchdb
sudo chown -R couchdb:couchdb /usr/local/{lib,etc}/couchdb /usr/local/var/{lib,log,run}/couchdb
sudo chmod -R g+rw /usr/local/{lib,etc}/couchdb /usr/local/var/{lib,log,run}/couchdb
# </pre>
</div>
</div>
The next step is downloading the source code and unpacking it:<br />
(find an appropriate mirror near you)<br />
<div>
<pre class="brush: java">#
# Download source and unpack
wget http://ftp-stud.hs-esslingen.de/pub/Mirrors/ftp.apache.org/dist/couchdb/source/1.6.0/apache-couchdb-1.6.0.tar.gz
tar xzf apache-couchdb-*.tar.gz
# </pre>
</div>
In order to start the "configure" and "make" process, switch into the couchDB directory:<br />
<div>
<pre class="brush: java">#
# Change into the couchDB directory
cd apache-couchdb-1.6.0
# </pre>
</div>
Now configure the build:<br />
<div>
<pre class="brush: java">#
#Configure the build
./configure --prefix=/usr/local --with-js-lib=/usr/lib --with-js-include=/usr/include/js --enable-init
# </pre>
</div>
When configure is through, you should see this message:<br />
<blockquote class="tr_bq">
“You have configured Apache CouchDB, time to relax.<br />
Run 'make && sudo make install' to install.”</blockquote>
This also tells you what the next step will be: running make and make install.<br />
<div>
<pre class="brush: java">#
# running make and make install
make && sudo make install
# </pre>
</div>
This will take a couple of minutes. But when its done, you have couchDB 1.6.0 compiled on your pi.<br />
<br />
Finally create some soft links, make the service start up at boot-time and start couchDB:<br />
<div>
<pre class="brush: java">#
# Start couchDB
sudo ln -s /usr/local/etc/init.d/couchdb /etc/init.d/couchdb
sudo /etc/init.d/couchdb start
sudo update-rc.d couchdb defaults
# see if its running...
curl http://127.0.0.1:5984/
# </pre>
</div>
<br />
As you can see, the couchDB service binds to localhost.<br />
If you want to reach couchDB from another machine, maybe from another Pi :-), change this.<br />
Open local.ini, find the [httpd] section, activate the binding_address and set it to the IP of your Pi:<br />
<div>
<pre class="brush: java">#
# make couchDB accessible within your network
sudo vi /usr/local/etc/couchdb/local.ini
# </pre>
</div>
Within this file, find the [httpd] section, activate bind_address and set it to 0.0.0.0<br />
It should now look like this:<br />
<br />
[httpd]<br />
;port = 5984<br />
bind_address = 0.0.0.0<br />
<br />
As a final test, re-boot your Pi and try to reach couchDB Futon:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-HJoMpzaZ-zs/U2_jc4I6rEI/AAAAAAAAAFk/hbs1BkjKPz8/s1600/Futon.JPG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="180" src="http://4.bp.blogspot.com/-HJoMpzaZ-zs/U2_jc4I6rEI/AAAAAAAAAFk/hbs1BkjKPz8/s1600/Futon.JPG" width="400" /></a></div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
And that's it. CouchDB 1.6.0 is running on your Pi.<br />
<div>
<br />
There is one additional step suggested by a kind reader:<br />
<div>
<pre class="brush: java">#
# Now including this hint sent by anonymous
# make couchdb user (created above) owner of local ini-file
sudo chown couchdb:couchdb /usr/local/etc/couchdb/local.ini
# </pre>
</div>
</div>
<h3>
Credits</h3>
<div>
<div>
The instructions above are based on an installation guide compiled by Dave Cottlehuber on the new couchDB Confluence Wiki. It describes the process of installing couchDB on a Debian system.<br />
The Wiki entry is <a href="https://cwiki.apache.org/confluence/display/COUCHDB/Debian" target="_blank">here</a>.</div>
I slightly adjusted this process to be working for the Pi too and now with the Erlang Solutions repository included, this guide is even closer to the Wiki than the one for version 1.5.1.</div>
<div>
<br /></div>
Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com21tag:blogger.com,1999:blog-1625776533566626066.post-2227862183562915922014-05-11T22:59:00.000+02:002014-05-18T19:56:44.026+02:00Installing CouchDB on the Raspberry Pi<h2>
Installing CouchDB on the Raspberry Pi</h2>
<h3>
Why concern yourself with CouchDB</h3>
There are a lot of noSQL databases out there. Most of them are designed to be working distributed across a network and thus are subject to the CAP Theorem, meaning they are positioned somewhere within the triangle of Consistency, Availability and Partition Tolerance.<br />
<blockquote class="tr_bq">
A beautiful article dealing with the CAP Theorem (and how this theorem is related to the first concert of the Sex Pistols) can be found <a href="http://julianbrowne.com/article/viewer/brewers-cap-theorem" target="_blank">here</a>.</blockquote>
Personally I tend to associate the CAP Theorem more with Meat Loaf's immortal Two Out Of Three Ain't Bad, because it all comes down to that you can only go for two of the CAP features, but never for all three at once.<br />
<br />
The theorem implies that a software developer or architect has to carefully select a database according to the needs of his application.<br />
I came across couchDB while researching for a client insisting on offline capability being a core feature of his application.<br />
If you want a database to solve your offline capability problem, you are looking for one with at least these features:<br />
<ul>
<li>distributed</li>
<li>clever replication patterns</li>
<li>eventual consistency</li>
</ul>
Of course you have to consider a lot more, like availability on different devices, replication protocol, security etc.<br />
<br />
Finally you have managed to replicate your data into even the remotest parts of this world, provided that the devices hosting your application are at least online every now and then.<br />
<br />
But what about the application itself? How can we keep this up to date in this offline scenario?<br />
<br />
Actually this is where couchDB really kicks in.<br />
On top of just being a database, couchDB can serve its own web applications called couchApps. And these Apps are actually stored as documents inside of couchDB. And as such they can be replicated.<br />
<br />
Nice feature, this.<br />
<br />
Hopefully this will fire up your interest in couchDB and make you curious for all its other features like being schema-less, offering a pure REST-API, employing map-and-reduce queries and last but not least being designed with running on small devices in mind.<br />
<br />
Small devices like, for instance, the Raspberry Pi.<br />
<br />
<h3>
How To Get CouchDB On The Pi</h3>
<div>
To install couchDB on your Pi, you have in fact two basic options:</div>
<div>
<ul>
<li>install the binary package from Debian repository (e.g. with apt-get)</li>
<li>build from source</li>
</ul>
</div>
<div>
At the time of this writing, the latest stable version of couchDB was 1.5.1.</div>
<div>
The binary package you will get from the Wheezy repository is 1.2.0-5.<br />
Its quite a way from 1.2... to 1.5...</div>
<div>
<br /></div>
<div>
If version 1.2 is all you need, just do this:</div>
<div>
<pre class="brush: java">sudo apt-get install couchdb</pre>
</div>
<div>
If you are a bit more ambitious, then read on.<br />
<br /></div>
<h3>
Building CouchDB On Your Pi</h3>
<div>
The following instructions are based on an installation guide compiled by Dave Cottlehuber on the new couchDB Confluence Wiki. It describes the process of installing couchDB on a Debian system.<br />
The Wiki entry is <a href="https://cwiki.apache.org/confluence/display/COUCHDB/Debian" target="_blank">here</a>.</div>
With just a little modification, this process works for the Pi too.<br />
<br />
The basic idea here is, to only build what is necessary, couchDB, and take what you can (Spidermonkey, Erlang and supporting libs) as binaries.<br />
<br />
But let's do this step by step.<br />
<h4>
Preparing the Pi</h4>
<div>
<ul>
<li>I used a brand new 16 GB card.</li>
<li>Download the latest Raspbian Wheezy from <a href="http://www.raspberrypi.org/downloads">www.raspberrypi.org/downloads</a><u><br /></u>At the time of this writing, the image version was 2014-01-07</li>
<li>Install the image on your pi and do the regular raspi-config</li>
<ul>
<li>Extend the partition</li>
<li>Set your locales</li>
<li>...</li>
<li>I did not change the default memory split</li>
</ul>
<li>Re-boot the pi for the partition extension to take effect</li>
<li>update and upgrade your installation</li>
</ul>
<div>
The pi is now ready.</div>
</div>
<h4>
Install all we need</h4>
<div>
The Wiki suggests to add the Cloudant repository for Spidermonkey and the Erlang Solutions repository for Erlang respectively.</div>
<div>
I tried this and encountered some problems on the Pi:</div>
<div>
<ul>
<li>The Erlang package you get this way is version 17+<br />The configure-process later complained about this version being out of range (I guess its too new)</li>
<li>The Cloudant repo lacked support armhf </li>
</ul>
<div>
On the pi you are better off with what the standard repository offers.</div>
</div>
<div>
Install the following packages:</div>
<div>
<pre class="brush: java"># Install lsb-release
sudo apt-get install lsb-release
# Install Compilers
sudo apt-get install erlang-nox
sudo apt-get install erlang-dev
# Install what else is needed
sudo apt-get install libmozjs185-1.0
sudo apt-get install libmozjs185-dev
sudo apt-get install libcurl4-openssl-dev
sudo apt-get install libicu-dev</pre>
</div>
<div>
Next we have to create an account for couchDB:<br />
<div>
<pre class="brush: java"># Create couchDB account
sudo useradd -d /var/lib/couchdb couchdb
sudo mkdir -p /usr/local/{lib,etc}/couchdb /usr/local/var/{lib,log,run}/couchdb /var/lib/couchdb
sudo chown -R couchdb:couchdb /usr/local/{lib,etc}/couchdb /usr/local/var/{lib,log,run}/couchdb
sudo chmod -R g+rw /usr/local/{lib,etc}/couchdb /usr/local/var/{lib,log,run}/couchdb
</pre>
</div>
</div>
The next step is downloading the source code and unpacking it:<br />
(find an appropriate mirror near your location)<br />
<div>
<pre class="brush: java"># Download source and unpack
wget http://ftp-stud.hs-esslingen.de/pub/Mirrors/ftp.apache.org/dist/couchdb/source/1.5.1/apache-couchdb-1.5.1.tar.gz
tar xzf apache-couchdb-*.tar.gz</pre>
</div>
In order to start the "configure" and "make" process, switch into the couchDB directory:<br />
<div>
<pre class="brush: java"># Change into the couchDB directory
cd apache-couchdb-1.5.1
</pre>
</div>
Now configure the build:<br />
<div>
<pre class="brush: java">#Configure the build
./configure --prefix=/usr/local --with-js-lib=/usr/lib --with-js-include=/usr/include/js --enable-init
</pre>
</div>
When configure is through, you should see this message:<br />
<blockquote class="tr_bq">
“You have configured Apache CouchDB, time to relax.<br />
Run 'make && sudo make install' to install.”</blockquote>
This also tells you what the next step will be: running make and make install.<br />
<div>
<pre class="brush: java"># running make and make install
make && sudo make install
</pre>
</div>
This will take a couple of minutes. But when its done, you have couchDB 1.5.1 compiled on your pi.<br />
<br />
Finally create some soft links, make the service start up at boot-time and start couchDB:<br />
<div>
<pre class="brush: java"># Start couchDB
sudo ln -s /usr/local/etc/init.d/couchdb /etc/init.d/couchdb
sudo /etc/init.d/couchdb start
sudo update-rc.d couchdb defaults
# see if its running...
curl http://127.0.0.1:5984/
</pre>
</div>
<br />
As you can see, the couchDB service binds to localhost.<br />
If you want to reach couchDB from another machine, maybe from another Pi :-), change this.<br />
On start up, couchDB reads its configuration in file chain that you can see by typing: <br />
<div>
<pre class="brush: java"># View config file chain
couchdb -c
</pre>
</div>
You should see something like this:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-Wd_UVa7EfCE/U29tP4UElxI/AAAAAAAAAFU/4rxbHSF-O6g/s1600/Filechain.JPG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-Wd_UVa7EfCE/U29tP4UElxI/AAAAAAAAAFU/4rxbHSF-O6g/s1600/Filechain.JPG" /></a></div>
<div>
<br /></div>
<br />
<br />
<br />
<br />
CouchDB first reads default.ini. Afterwards these settings can be enriched or overwritten by the local.ini setting.<br />
Documentation suggests to change local.ini for default.ini might be overwritten by upgrade or re-installation.<br />
Open local.ini, find the [httpd] section, activate the binding_address and set it to the IP of your Pi:<br />
<div>
<pre class="brush: java"># make couchDB accessible within your network
sudo vi /usr/local/etc/couchdb/local.ini.
</pre>
</div>
As a final test, re-boot your Pi and try to reach couchDB Futon:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-HJoMpzaZ-zs/U2_jc4I6rEI/AAAAAAAAAFk/hbs1BkjKPz8/s1600/Futon.JPG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-HJoMpzaZ-zs/U2_jc4I6rEI/AAAAAAAAAFk/hbs1BkjKPz8/s1600/Futon.JPG" height="180" width="400" /></a></div>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
And that's it. CouchDB 1.5.1 is running on your Pi.<br />
<br />
PS<br />
This blog is call Playing JEE On The Pi, but the next entries will probably be more JavaScript than Java. I hope you don't mind...<br />
<br />
<br />
<br />Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com2tag:blogger.com,1999:blog-1625776533566626066.post-17935439698810837322013-09-21T21:00:00.000+02:002016-02-15T11:54:49.905+01:00Some new JEE6 Features: Asynchronous EJBs, Singletons and Futures<h2>
Asynchronous EJB Methods, Singletons and Futures</h2>
<div class="MsoNormal">
In this
post I want to explore some new JEE6 features concerning EJBs and run them on
the Raspberry Pi.</div>
<div class="MsoNormal">
<span lang="EN-US">These
features are</span><br />
<br />
<ul>
<li><span style="text-indent: -18pt;">Singleton
EJBs</span></li>
<li><span style="text-indent: -18pt;">Asynchronous
EJB calls</span></li>
<li><span style="text-indent: -18pt;">Asynchronous
EJB calls returning Futures</span></li>
</ul>
</div>
<div class="MsoNormal">
<h3>
<span lang="EN-US">What are these
elements?</span></h3>
</div>
<div class="MsoNormal">
<span lang="EN-US">Singleton
EJB<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">As the name
indicates, a Singleton EJB is a bean that will be instantiated exactly once in
your application. Does not sound very
spectacular but in fact it is. Whenever you will look up this bean you will
always get the same instance. This goes for every client of the application. <o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">Asynchronous
EJB calls<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">It is now
possible to code EJB methods that can be called asynchronously. You call the
method and it returns immediately while it performs its task in the background.
This is calling the method “Send-and-Forget” style. Just like sending a message
via JMS.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">If you want
to keep in touch with the method, your method needs to return a Future object.<o:p></o:p></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">Asynchronous
EJB calls returning Futures<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">When an
asynchronous method returns a Future, you can keep in touch. The Future is your
link to the running method. It offers an API to determine whether the method’s
result is already available or not. If a value is ready to be retrieved you can
fetch it.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Futures are
very handy concept for certain applications. You can trigger a couple of
calculations running concurrently, collect the Futures and scan them for
results subsequently trickling in. <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">There is
one little drawback with using Futures in the context of EJBs: Future is just
an interface and the Java default implementation AsyncResult is not
serializable. An AsyncResult cannot be passed over Remote EJB interfaces. Using
Future and AsyncResult requires you to carefully choose between local and
remote interfaces and to modularize your EJBs properly.</span></div>
<div class="MsoNormal">
<h3>
Demo Application</h3>
</div>
<div class="MsoNormal">
<span lang="EN-US">Let’s have
a look on the demo application.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">The next
picture will give an overview:<o:p></o:p></span></div>
<div class="MsoNormal">
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-RjZ6fSlFAt0/Uj3tz4_ZLNI/AAAAAAAAAEg/ByhbgRL5bSU/s1600/AppModel.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-RjZ6fSlFAt0/Uj3tz4_ZLNI/AAAAAAAAAEg/ByhbgRL5bSU/s1600/AppModel.jpg" height="233" width="320" /></a></div>
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">The whole
thing consists of two JEE applications. A servlet and an ejb-module. Both are
separate deployment units:</span><br />
<ul>
<li><span style="text-indent: -18pt;">Servlet:
LocalAsyncClient</span></li>
<li><span style="text-indent: -18pt;">Ejb-module:
MyAsyncExample</span></li>
</ul>
</div>
<div class="MsoNormal">
<span lang="EN-US">All the new
features are coded within the ejb.module. The servlet is only for triggering
the action and for displaying the results.</span></div>
<div class="MsoNormal">
<h3>
<span lang="EN-US">Coding</span></h3>
</div>
<div class="MsoNormal">
<span lang="EN-US">The
application is best described inside out, so let’s start with the heart of the
matter, the ejb that offers asynchronous methods.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">This bean
is called RandomLengthTask. Here is the local interface:<o:p></o:p></span></div>
<pre class="brush: java">package com.mtag.ejbs.async;
import java.util.concurrent.Future;
import javax.ejb.Asynchronous;
import javax.ejb.Local;
/**
* EJB’s local interface.
*/
@Local
public interface RandomLengthTaskLocal {
/**
* This annotation is all it takes to make this function asynchronous.
* When it is called it will return the Future immediately.
*/
@Asynchronous
public Future<string> performRandomTask();
/**
* Asynchronous but not returning anything.
* Will return immediately.
*/
@Asynchronous
public void sendAndForget(long duration);
}
</pre>
<div class="MsoNormal">
<span lang="EN-US">As can be
seen, turning a regular method into an asynchronous one does not take much.
It’s just one annotation.<o:p></o:p></span></div>
<br />
<div class="MsoNormal">
<span lang="EN-US">Returning a
Future is notable. The method returns it directly and only later fills in the
value. The client can question the Future and eventually extract the result.<o:p></o:p></span><br />
The Future is the reason for providing a local interface for our EJB. We cannot pass a Future over a remote connection. We need to structure our application accordingly. More on this structure later.<br />
Let'ts take a look at the EJB's implementation:<br />
<pre class="brush: java">package com.mtag.ejbs.async;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Random;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.Stateless;
/**
* Session Bean implementation class RandomLengthTask
*/
@Stateless
public class RandomLengthTask implements RandomLengthTaskLocal {
private static final Logger LOGGER = Logger.getLogger(RandomLengthTask.class.getName());
/**
* Default constructor.
*/
public RandomLengthTask() {
// TODO Auto-generated constructor stub
}
/**
*
* @param duration
* @return void
* Asynchronous Method returning immediately without a result value
*/
@Asynchronous
public void sendAndForget(long duration) {
LOGGER.info("Send and Forget will take: " + duration + "ms");
try {
Thread.sleep(duration);
LOGGER.info("Send and Forget: Waking up. Slept " + duration + "ms");
} catch (InterruptedException e) {
LOGGER.info("Send and Forget: Sleep was interrupted!");
}
LOGGER.info("Send and Forget has finished");
}
/**
* Will return a Future immediately.
* Eventually the .
* The length of the task will be randomly generated between 1 second and 1 minute.
*/
@Asynchronous
public Future<String> performRandomTask() {
// record starting time...
Calendar cal = Calendar.getInstance();
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
String started = sdf.format(cal.getTime());
LOGGER.info("RandomTask started at: "+started);
try {
Thread.sleep(new Random(System.currentTimeMillis()).nextInt(60000)+1000);
} catch (InterruptedException e) {
LOGGER.info("RandomTask was interrupted...");
}
cal = Calendar.getInstance();
LOGGER.info("RandomTask finished at: "+sdf.format(cal.getTime()));
//
// AsyncResult is the default implementation of the Future interface...
return new AsyncResult<String>("Random Task Start: "+started+" / finished: "+sdf.format(cal.getTime()));
}
}
</pre>
The life of the sendAndForget method is a bit hard to monitor. After calling it lives a life of its own. Best we can do is to log its activities. Later, when running the servlet, we have to take a look at the console output once in a while.<br />
The method takes the duration it is supposed to run as a parameter. We will set this duration from the servlet later.<br />
<br />
The performRandomTask is a bit more interesting. It not only returns a Future (in fact it returns an AsyncResult which is the default implementation of Future) but generates its own duration randomly.<br />
When finishing, it returns its start and finish time stamps as a result into the Future. This information can of course be accessed by our servlet.<br />
<br />
We now have a bean that offers two types of asynchronous methods: a send-and-forget type returning void immediately and one returning a Future immediately supplying a value into that Future in the future :-).<br />
The bean has a local interface because we cannot transport the Future over a remote call.<br />
We will now write a Decorator bean wrapping the local interface and offering a remote one to the public.<br />
This bean is called InBetweenBean as can be seen in the picture above.<br />
Here is the code of its remote interface:<br />
<pre class="brush: java">package com.mtag.ejbs.async;
import java.util.List;
import javax.ejb.Remote;
@Remote
public interface InBetweenBeanRemote {
/**
* Calls an asynchronous method and saves the result, a Future, in the
* SessionManager Bean.
*/
public void callLenghyTask(String sID);
/**
* Calls an asynchronous send-and-forget method with an instruction of how long to
* sleep (duration)
*/
public void sendAndForgetTask(long duration);
/**
* Queries the SessionManagerBean for the results asspciated with a giben client (sid)
* and returns a list of results.
* @param sid
* @return
*/
public List<string> getResultList(String sid);
}
</pre>
</div>
<br />
The method "sendAndForgetTask" is easy. It calls our asynchronous send-and-forget method returning void immediately. The amount of time this method shall run must be supplied by the client.<br />
"callLegthyTask" is more interesting a we will see in a minute. It takes a session-id for a parameter which indicates to some session state that has to be handled.<br />
The same goes for the "getResultList" method. It will return a session specific list of results.<br />
<br />
Let's have a look at the implementation of this interface.<br />
<pre class="brush: java">package com.mtag.ejbs.async;
import java.util.List;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import com.mtag.ejbs.async.SessionManagerLocal;
/**
* Session Bean implementation class InBetweenBean
*/
@Stateless
public class InBetweenBean implements InBetweenBeanRemote {
private static final Logger LOGGER = Logger.getLogger(InBetweenBeanRemote.class.getName());
@EJB(mappedName = "java:global/MyAsyncExample/RandomLengthTask!com.mtag.ejbs.async.RandomLengthTaskLocal")
RandomLengthTaskLocal rltBean;
@EJB(mappedName = "java:global/MyAsyncExample/SessionManager!com.mtag.ejbs.async.SessionManagerLocal")
SessionManagerLocal sessionManager;
/**
* Default constructor.
*/
public InBetweenBean() {
// TODO Auto-generated constructor stub
}
/**
* Calls an asynchronous task returning a Future.
* This Futute is stored within the SessinManager Singleton EJB
*/
public void callLenghyTask(String sID) {
LOGGER.info("calling RLTask...");
Future<String> result = rltBean.performRandomTask();
//
// Get SessionState for this Client...
SessionState ss = sessionManager.getSessionState(sID);
// Add the Future...
ss.addResult(result);
// Give StateObjekt back to SessionManager...
sessionManager.addSessionState(sID, ss);
LOGGER.info("RLTask has finished...");
}
/**
* Calls a send-and-forget task...
*/
public void sendAndForgetTask(long duration) {
LOGGER.info("calling SendAndForget-Task...");
rltBean.sendAndForget(duration);
LOGGER.info("SendAndForgetTask finished...");
}
/**
* Returns the SessionState of a given client as String-List.
* A String-List is serializable...
* @param sid
* @return
*/
public List<String> getResultList(String sid) {
SessionState ss = sessionManager.getSessionState(sid);
return ss.getResultsAsStringList();
}
}
</pre>
<br />
The first thing I'd like to mention here, are the two @EJB-annotations.<br />
They are injecting EJB references into the respective local variables. Do you still remember how this had to be done in the early days of J2EE? You had to maintain monstrous xml-files describing your beans and including all the references form one to the other. Now one annotation does it all. I don't even have an xml-file to maintain explicitly in this project (though I'm not quite sure if I can keep this up during the course of this blog). Anyway, JEE has come a long way.<br />
In case you ask yourself where theses JNDI-names come from, the JEE specification defines some of those "global" ones. Others are defined by JBoss. One sure way to have them right is to deploy the beans you already implemented. JBoss publishes the names on the console and wrights them to the log-files.<br />
<br />
Ok, now we have a reference on the bean that implements the asynchronous methods and a reference on on a bean called SessionManager.<br />
We'll discuss the SessionManager bean later in more detail, because it is another new JEE feature.<br />
For now just take a look at the implementation of the "callLengthyTask" method.<br />
First we call an asynchronous method receiving a Future.<br />
Then we call the Session Manager for the session state of the current client, identified by its session id.<br />
Next we add the Future to this session state and store the session state back to the Session Manager.<br />
Eventually the Future we've just given away to the Session Manager will be supplied with a value from the concurrently running asynchronous method.<br />
To see these values, a client can call the "getResultList" method of our InBetweenBean.<br />
The getResultList-method calls the Session Manager for this client's session state and returns this state as a serializable list of strings. This list can be transported over a remote connection like a remote EJB call.<br />
As you can see, InBetweenBean does not much but wraps functionality supplied by others.<br />
<br />
Taking a look at the SessionManager bean and its little POJO helper SessionState wraps up the ejb-module.<br />
<br />
Here are interface and implementation of the SessionManager EJB:<br />
<pre class="brush: java">package com.mtag.ejbs.async;
import javax.ejb.Local;
@Local
public interface SessionManagerLocal {
/**
* @param sessionId
* @return SessionState
*/
public SessionState getSessionState(String sessionId);
/**
* Adds a SessionState to the Hashmap of
* @param sid
* @param ss
*/
public void addSessionState(String sid, SessionState ss);
}
</pre>
<br />
<pre class="brush: java">package com.mtag.ejbs.async;
import java.util.HashMap;
import javax.ejb.Singleton;
import com.mtag.ejbs.async.SessionState;
/**
* Manages a Hashmap of SessionState objects.
* Key to the hashmap is a client's session id.
*
*/
@Singleton
public class SessionManager implements SessionManagerLocal {
private HashMap<String, SessionState> sessions = new HashMap<String, SessionState>();
/**
* Default constructor.
*/
public SessionManager() {
// TODO Auto-generated constructor stub
}
/**
* Store SessionState in the Hashmap
* @param sid clients session id
* @param ss SessionState object
*/
public void addSessionState(String sid, SessionState ss) {
sessions.put(sid, ss);
}
/**
* Retrive the session state of a given client.
* If this is the first call a clien, a session state object will
* created, stored and returned.
* @param sessionId
* @return SessionState
*/
public SessionState getSessionState(String sessionId){
SessionState state = null;
state = sessions.get(sessionId);
if (state == null) {
state = new SessionState();
sessions.put(sessionId, state);
}
return state;
}
}</pre>
And finally the SessionState class: a POJO maintained by the SessionManager:
<br />
<pre class="brush: java">package com.mtag.ejbs.async;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
*
* @author vkoster
*
* Store for Session State, both
* - Session State and
* - Resource State
* Substitutes a little database
*/
public class SessionState implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private List<Future<String>> myList = new ArrayList<Future<String>>();
public void addResult(Future<String> f) {
myList.add(f);
}
/**
* Transform a list of Futures into a list of strings
* @return
*/
public List<String> getResultsAsStringList() {
List<String> rl = new ArrayList<string>();
for (Future<String> f : myList) {
try {
if (f.isDone()){
rl.add(f.get());
} else {
rl.add("No reuslt yet!");
}
} catch (CancellationException e) {
rl.add("Calculatioin cancled!");
} catch (ExecutionException e) {
rl.add("Execution error!");
} catch (InterruptedException e) {
rl.add("Calculation interrupted!");
}
}
return rl;
}
}
</string></pre>
<span lang="EN-US"><br /></span>
<span lang="EN-US">The SessionManager bean is annotated as @Singleton.</span><br />
<span lang="EN-US">This is all it takes to accomplish the following:</span><br />
<ul>
<li>The EJB is a singleton (obviously)</li>
<li>There will only be one instance of this bean, regardless of how many clients will access it</li>
<li>All clients will get exactly this one instance when they call this bean</li>
<li>The bean is comparable to a process with identity and addressability</li>
</ul>
<div>
Who is responsible for managing all this concurrency?</div>
<div>
If you don't say otherwise, the ejb-container will do it for you. This is called "Container Managed Concurrency". If you prefer to take things into your own hands, there are annotations for this too.</div>
<div>
I can do with container managed concurrency in this example.</div>
<div>
<br /></div>
<div>
A Singleton comes in handy for our purposes. It stores the session state of all the clients concurrently using out asynchronous ejb-module.<br />
Whenever a client uses our ejb-module by calling a method that returns a Future, we add this Future to the session state of this client and store it within the Singleton. All the client has to do is to supply a Session ID to identify itself in subsequent calls.<br />
<br />
The Session State itself is a simple List of Futures.<br />
Remember: these Futures will receive their results eventually.<br />
SessionState offers to translate the current state of this list of Futures into a String-list.<br />
The String-list can be sent to remote clients which then can visualize the current state of the session.<br />
<h2>
Deployment</h2>
</div>
<div>
<br /></div>
<div>
<br /></div>
<span lang="EN-US">...to be continued...</span>Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com0tag:blogger.com,1999:blog-1625776533566626066.post-61482920390291129732013-08-18T21:18:00.000+02:002013-08-18T21:21:19.196+02:00Running JBoss AS7 on Raspberry Pi<h2>
<span lang="EN-US" style="font-size: 11pt; line-height: 115%;"><span style="font-family: inherit;">Running
JBoss</span></span></h2>
<h3>
<span lang="EN-US"><span style="font-family: inherit;">Choose a
Mode</span></span></h3>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">The JBoss
installation on our Pi offers a couple of different configurations out of the
box.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">The first
choice to be taken is whether to start JBoss in standalone or domain mode. <o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">Doman mode
is a special management mode which we might inspect later. For now we will set
off in standalone mode. This just means we will have to configure each instance
individually. We can still cluster our standalone instances and even go for
high availability in standalone mode.</span></span></div>
<h3>
<span lang="EN-US"><span style="font-family: inherit;">Choose a
Configuration</span></span></h3>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">All the scripts
for starting JBoss are located in JBOSS_HOME/bin.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">JBoss is
started in standalone mode by running standalone.sh:<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">standalone.sh
<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">Without any
parameters, standalone.sh will use the configuration defined in standalone.xml,
located in JBOSS_HOME/standalone/configuration directory.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">You can
choose another configuration by specifying it on the command line: <o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">standalone.sh
–server-config=standalone-full.xml<o:p></o:p></span></span></div>
<div class="MsoNormal">
<br /></div>
<span lang="EN-US" style="font-size: 11pt; line-height: 115%;"><span style="font-family: inherit;">
<span lang="EN-US" style="font-size: 11pt; line-height: 115%;">My approach is to backup standlone.xml and to edit
the original according to my needs. I’m going for standalone.xml to have a
small footprint to start with. Additional features can be included later, either
by editing standalone.xml or by using the management console or the command
line interface CLI.</span></span></span><br />
<h3>
<span lang="EN-US">JBoss and
Java</span></h3>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">Standalone.sh
inspects first JAVA then JAVA_HOME environment variable for a hint, where java
is installed. If both variables are not set, JAVA is simply set to java which
means the PATH variable determines if and where Java will be found.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">And it will
be found like this:<o:p></o:p></span></span></div>
<ul style="margin-top: 0cm;" type="disc">
<li class="MsoNormal"><span lang="EN-US"><span style="font-family: inherit;">We find /usr/bin in our classpath (verify
by typing $PATH on a Pi console)<o:p></o:p></span></span></li>
<li class="MsoNormal"><span lang="EN-US"><span style="font-family: inherit;">/usr/bin contains “java”<o:p></o:p></span></span></li>
<li class="MsoNormal"><span lang="EN-US"><span style="font-family: inherit;">Java is a softlink into alternatives (/etc/alternatives/java)<o:p></o:p></span></span></li>
<li class="MsoNormal"><span lang="EN-US"><span style="font-family: inherit;">Following this link, we find: java ->
/usr/lib/jvm/java-7-openjdk-armel/jre/bin/java<o:p></o:p></span></span></li>
</ul>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">And that is
what we get when we run Java on the Pi.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">Everything
depends on soft links and the alternatives concept. This makes it very easy to
switch to another Java installation in the future.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">To make a
long story short, leave everything as it is.</span></span></div>
<h3>
<span lang="EN-US">Making
JBoss available within Your Network</span></h3>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">All my Pis
have static IP addresses within my private network. This can be configured
within your router.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">If you want
to be able to reach a JBoss instance running on a Pi, you have to configure
JBoss’ “Public Interface”.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">This
configuration is done standalone.xml located in
/JBOSS_HOME/standalone/configuration.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">Find the
interfaces section and edit the public and the management interface like this:</span><o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Courier New, Courier, monospace;"><interfaces><o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Courier New, Courier, monospace;">
<interface name="management"><o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Courier New, Courier, monospace;"> <inet-address
value="xxx.xxx.xxx.xxx"/><o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Courier New, Courier, monospace;">
</interface><o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Courier New, Courier, monospace;">
<interface name="public"><o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Courier New, Courier, monospace;"> <inet-address
value="xxx.xxx.xxx.xxx"/><o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Courier New, Courier, monospace;">
</interface><o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Courier New, Courier, monospace;">…<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Courier New, Courier, monospace;"></interfaces></span></span><span lang="EN-US"> <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">Where xxx.xxx.xxx.xxx
is the IP you configured in your rooter for this specific Pi.<o:p></o:p></span></span></div>
<span lang="EN-US" style="font-size: 11pt; line-height: 115%;"><span style="font-family: inherit;">
</span></span><br />
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">Save the
changes.</span><o:p></o:p></span></div>
<h3>
<span lang="EN-US">Create
JBoss Users</span></h3>
<div class="MsoNormal">
<span lang="EN-US">In order to
access JBoss’ admin console, we need to create a management-realm user.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">This is
done by running the add-user.sh script located in /JBOSS_HOME/bin directory.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">
<span lang="EN-US" style="font-family: "Calibri","sans-serif"; font-size: 11.0pt; line-height: 115%; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-language: AR-SA; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;">Be sure to select management realm.</span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-DxyRAw0exu0/UhEbQEFEemI/AAAAAAAAADg/6ui9ophBEeo/s1600/create_mgnt_user.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-DxyRAw0exu0/UhEbQEFEemI/AAAAAAAAADg/6ui9ophBEeo/s1600/create_mgnt_user.png" height="104" width="320" /></a></div>
<div class="MsoNormal">
<span lang="EN-US"><span lang="EN-US" style="font-family: "Calibri","sans-serif"; font-size: 11.0pt; line-height: 115%; mso-ansi-language: EN-US; mso-ascii-theme-font: minor-latin; mso-bidi-font-family: "Times New Roman"; mso-bidi-language: AR-SA; mso-bidi-theme-font: minor-bidi; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US; mso-fareast-theme-font: minor-latin; mso-hansi-theme-font: minor-latin;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">For later
use, we create an application-realm user.<o:p></o:p></span></div>
<div class="MsoNormal">
</div>
<div class="MsoNormal">
<span lang="EN-US">For now,
assign the guest role to this user.</span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-ZQWWJB2pB3E/UhEbsnm0BtI/AAAAAAAAADo/-7PxHcX5pEA/s1600/create_app_user.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-ZQWWJB2pB3E/UhEbsnm0BtI/AAAAAAAAADo/-7PxHcX5pEA/s1600/create_app_user.png" height="116" width="320" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<h3>
<span lang="EN-US">Starting
JBoss</span></h3>
<div class="MsoNormal">
<span lang="EN-US">Let’s give
JBoss a test run.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Switch to /JBOSS_HOME/bin
and run standalone.sh in a Pi terminal window:<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">
</span></div>
<div class="MsoNormal">
<span lang="EN-US">sh
standalone.sh<o:p></o:p></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-N7w0izpkiSQ/UhEcS5_N4UI/AAAAAAAAAD0/QRKFWJEakjs/s1600/jboss_start.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-N7w0izpkiSQ/UhEcS5_N4UI/AAAAAAAAAD0/QRKFWJEakjs/s1600/jboss_start.png" height="201" width="320" /></a></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">When JBoss
is up, you can access the web-server from any client within your network.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">
</span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">If you
assigned a name to your Pi within your router, you can use this name to access
the Pi. If not, use the Pi’s IP address.</span><o:p></o:p></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-6DjjXft6Z9A/UhEcpKRRLjI/AAAAAAAAAD8/Qjz8leNsPfE/s1600/jboss_browser.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-6DjjXft6Z9A/UhEcpKRRLjI/AAAAAAAAAD8/Qjz8leNsPfE/s1600/jboss_browser.png" height="191" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
From here,
you should be able to access the management console. As credentials, enter the
admin-user you just created using add-user script.<br />
<div class="MsoNormal">
<span lang="EN-US"><o:p></o:p></span></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-caG3kMJQzDo/UhEcsuR9Z2I/AAAAAAAAAEI/3yXZqCxKXws/s1600/mgnt_console.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://4.bp.blogspot.com/-caG3kMJQzDo/UhEcsuR9Z2I/AAAAAAAAAEI/3yXZqCxKXws/s1600/mgnt_console.png" height="243" width="320" /></a></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span></div>
<h3>
<span lang="EN-US">Stopping
JBoss</span></h3>
<div class="MsoNormal">
<span lang="EN-US">The best
way to stop JBoss is by using the CLI.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Open another
terminal session on the Pi and switch to /JBOSS_HOME/bin.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Run <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">jboss-cli.sh<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">The CLI
starts in “disconnected” mode, so you have to connect.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Type
connect xxx.xxx.xxx.xxx (IP address of your Pi)<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">When
connected successfully, enter <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">:shutdown<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">
</span></div>
<div class="MsoNormal">
<span lang="EN-US">JBoss will
shut down and you can exit the terminal session.<o:p></o:p></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-RRS_hbrSbmo/UhEcwU4JeTI/AAAAAAAAAEQ/uXfXPuWqTqI/s1600/cli_shutdown.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-RRS_hbrSbmo/UhEcwU4JeTI/AAAAAAAAAEQ/uXfXPuWqTqI/s1600/cli_shutdown.png" height="201" width="320" /></a></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<div class="MsoNormal">
<span lang="EN-US"><br /></span></div>
<h3>
<span lang="EN-US">Summary</span></h3>
<div class="MsoNormal">
<span lang="EN-US">Now we have Java and JBoss installed on our Pi(s). <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">JBoss is now accessible from anywhere within our network.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">We created a management-user and an application-user.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">We can start and stop JBoss on the Pi and have access to its management
console.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">In the next post we will develop our first JEE application and deploy it
to the Pi.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">
</span></div>
<div class="MsoNormal">
<span lang="EN-US">See you then…<o:p></o:p></span></div>
Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com2tag:blogger.com,1999:blog-1625776533566626066.post-60024236799021979032013-08-13T22:41:00.001+02:002013-08-13T23:17:57.512+02:00Side Note: Accessing the Pi<h2>
<span lang="EN-US">Accessing
the Pi</span></h2>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">This blog
assumes that your Pi is connected to a network, preferably with access to the
internet for easy updates. But of course, everything discussed here can be done
in a closed network too.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">Apart from
the network you don’t need anything. No monitor, no keyboard.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">You can
easily access your Pi remotely when you enable the ssh service during setup.<o:p></o:p></span></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">From now on
you can access it from any client within your network.</span></span></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: inherit;">Use ssh
from any Linux client or putty from a windows client.</span><o:p></o:p></span><br />
<span lang="EN-US"><span style="font-family: inherit;"><br /></span></span>
<div class="MsoNormal">
<span lang="EN-US">Let’s do something
practical: Connecting to the Pi from a windows client and checking the Java
installation.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Power up
your Pi.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Open a
putty session on a windows machine, selecting ssh as connection type.<o:p></o:p></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-w5RoV_rt6HI/UgqhJVjUvMI/AAAAAAAAADI/UocwTNdBeTI/s1600/putty.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://1.bp.blogspot.com/-w5RoV_rt6HI/UgqhJVjUvMI/AAAAAAAAADI/UocwTNdBeTI/s1600/putty.png" height="307" width="320" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span lang="EN-US">This will
start a terminal window where you will have to enter your credentials.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Check the
Java installation by typing:<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Courier New, Courier, monospace;">java -version</span><o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">You should
see something like this:<o:p></o:p></span></div>
<a href="http://3.bp.blogspot.com/-ZcQa3_QWIR8/UgqhdnP40OI/AAAAAAAAADU/JHbOCQX87-U/s1600/check_java.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" src="http://3.bp.blogspot.com/-ZcQa3_QWIR8/UgqhdnP40OI/AAAAAAAAADU/JHbOCQX87-U/s1600/check_java.png" height="251" width="400" /></a><br /><div class="MsoNormal">
<span lang="EN-US">Ok, openJDK seems to be installed properly.</span><span lang="EN-US"><o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">In order to
shut your pi down, type:<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US"><span style="font-family: Courier New, Courier, monospace;">sudo shutdown
–h now</span><o:p></o:p></span></div>
<span lang="EN-US">
</span><br />
<div class="MsoNormal">
<span lang="EN-US">and close
the terminal window.<o:p></o:p></span></div>
</div>
Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com0tag:blogger.com,1999:blog-1625776533566626066.post-79337253714158701712013-08-08T15:16:00.002+02:002013-08-13T22:49:47.870+02:00Setting up the Raspberry Pi installing Debian. Java and JBoss<h2>
<span lang="EN-US">Setting up
the Pi <div class="separator" style="clear: both; text-align: center;">
<a href="http://www.raspberrypi.org/" imageanchor="1" style="clear: right; float: right; margin-bottom: 1em; margin-left: 1em;"><img border="0" src="http://www.raspberrypi.org/wp-content/uploads/2012/03/Raspi_Colour_R.png" height="200" width="165" /></a></div>
</span></h2>
<span lang="EN-US">Before we can start playing JEE on the Raspberry Pi, a few things need to be set up.</span><br />
<span lang="EN-US">As this is not too interesting, I try to cover it in a hurry. Please leave a note if you want one topic or the other dealt with in more detail.</span><br />
<h3>
<span lang="EN-US">What this
post will cover:</span></h3>
<ul style="margin-top: 0cm;" type="disc">
<li class="MsoNormal"><span lang="EN-US">Choosing and installing a suitable
operating system for your Pi<o:p></o:p></span></li>
<li class="MsoNormal"><span lang="EN-US">Installing Java on the Pi<o:p></o:p></span></li>
<li class="MsoNormal"><span lang="EN-US">Installing JBoss on the Pi<o:p></o:p></span></li>
<li class="MsoNormal"><span lang="EN-US">Setting up the Pi network<o:p></o:p></span></li>
</ul>
<h3>
<span lang="EN-US">Choosing
and installing a suitable operating system for your Pi</span></h3>
<div class="MsoNormal">
<span lang="EN-US">The first
task now is to choose and install a suitable operating system for your Pi.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">There are
several options available on the download page of the Raspberry Pi Foundation: <a href="http://www.raspberrypi.org/downloads">http://www.raspberrypi.org/downloads</a><o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">We go for
Soft-float Debian “wheezy” for the reasons described on the page:<o:p></o:p></span></div>
<div class="MsoNormal">
<blockquote class="tr_bq">
<span style="background-color: #999999;"><span lang="EN-US">“</span><span lang="EN-US" style="color: #333333; font-size: 10.0pt; line-height: 115%; mso-ansi-language: EN-US;">This image is identical to the Raspbian “wheezy” image, but uses the
slower soft-float ABI. It is only intended for use with software such as the
Oracle JVM which does not yet support the hard-float ABI used by Raspbian.”</span></span></blockquote>
</div>
<div class="MsoNormal">
<span lang="EN-US"> If we want to run java, what we do, we must
stick to an operating system offering a soft-float ABI.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">I tried the
Java8 preview offered by Oracle here: <a href="http://jdk8.java.net/download.html">http://jdk8.java.net/download.html</a>
which is supposed to support the hard-float ABI. But sorry, this works only
when running Java in client mode. As soon as your software needs server-mode,
you are out of the game.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">This is
also true for the embedded version of the Standard Edition.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Do we need
server mode? Yes, because software like JBoss AS7 needs it.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">So as of
now (at the time of this writing) the soft-float Debian “wheezy” seems like a
good idea for starting off:<o:p></o:p></span></div>
<ul style="margin-top: 0cm;" type="disc">
<li class="MsoNormal"><span lang="EN-US">Download soft-float Debian “wheezy” image<o:p></o:p></span></li>
<li class="MsoNormal"><span lang="EN-US">Bring the image to your sd-card<o:p></o:p></span></li>
<li class="MsoNormal"><span lang="EN-US">Start the Pi and run through the
config-script that will start up on first boot<o:p></o:p></span></li>
</ul>
<div class="MsoNormal">
<span lang="EN-US">Hint: The
config-script now offers to assign a hostname to your Pi. It is a good idea to
make use of this. Later, when you have a couple of console windows open on your
desktop machine pointing to different Pis, you will be glad to see meaningful
prompts identifying the Pis respectively (of course you can change the hostname
of your Pi later, but using the config-script is easier). <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Later, when
plugging your Pis into your network, you want to add these names to your
router’s configuration.</span></div>
<div class="MsoNormal">
<h3>
<span lang="EN-US">Installing
Java JDK on the Pi</span></h3>
</div>
<div class="MsoNormal">
<span lang="EN-US">The next
step is installing Java on your Pis.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Software
like the Oracle Java 8 preview would have to be installed manually.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">The regular
Java versions are maintained in the Debian software repositories and can easily
be installed by Debians package manager.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">I chose
this package and installed it with apt-get.<o:p></o:p></span><br />
<span lang="EN-US">Open up a console and enter:</span><br />
<span lang="EN-US"><br /></span>
<span lang="EN-US" style="font-family: Courier New, Courier, monospace;">sudo apt-get install openjdk-7-jdk</span><br />
<span lang="EN-US" style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div class="MsoNormal">
After the dust has settled, you have an openJDK 7 on your Pi.</div>
<div class="MsoNormal">
<h3>
<span lang="EN-US">Installing
JBoss AS7 on the Pi</span></h3>
</div>
<div class="MsoNormal">
<span style="font-family: inherit;">When installing JBoss, a</span> little bit more manual effort is required. Here is what you have to do:<br />
<h4>
Step 1: Download the package</h4>
<div>
At the time of this writing, JBoss AS7 was available in version 7.1.1.Final.</div>
<div>
You can download this package from the console using wget:</div>
<div>
<br />
<span style="font-family: Courier New, Courier, monospace;">sudo wget http://download.jboss.org/jbossas/7.1/jboss-as\</span><br />
<span style="font-family: Courier New, Courier, monospace;"> -7.1.1.Final/</span><span style="font-family: 'Courier New', Courier, monospace;">jboss-as-7.1.1.Final.tar.gz</span><br />
<br />
<h4>
<span style="font-family: inherit;">Step 2: Extract the download</span><span style="font-family: 'Courier New', Courier, monospace;"> </span></h4>
<span style="font-family: inherit;">Now that we have the tar-file on the pi we have to extract it into a suitable directory.</span><br />
<span style="font-family: inherit;">/usr/local is a good choice:</span><br />
<span style="font-family: inherit;"><br /></span>
<span style="font-family: Courier New, Courier, monospace;">sudo tar zxvf jboss-as-7.1.1.Final.tar.gz -C /usr/local</span><br />
<br />
(don't forget the -C option or the necessary directories will not be created)<br />
<h4>
Step 3: Smoothen the installation directory</h4>
JBoss AS7 is now installed in<span style="font-family: Courier New, Courier, monospace;"> /usr/local/jboss-as-7.1.1.Final</span><br />
If you find this to be a bit unhandy, change it like this:<br />
<br />
<span style="font-family: Courier New, Courier, monospace;">sudo mv jboss-as-7.1.1.Final/ jboss7</span><br />
<h4>
<span style="font-family: inherit;">Step 4: Make pi the owner of the installation</span></h4>
</div>
<div>
<span style="font-family: inherit;">I like my pi user to be the owner of the installation.</span></div>
<div>
<span style="font-family: inherit;">You can do this with the Change Owner command:</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;">sudo chown -R pi:pi /usr/local/jboss7/</span></div>
<div>
<span style="font-family: Courier New, Courier, monospace;"><br /></span></div>
<div>
<span style="font-family: inherit;">JBoss AS7 is now installed and ready to run on your Raspberry Pi.</span></div>
<div>
<span style="font-family: inherit;">Quite some heavy lifting for a little Pi like that. But we'll see JBoss behaves itself very well.</span></div>
</div>
<div class="MsoNormal">
<h3>
Network configuration</h3>
</div>
<div class="MsoNormal">
<span lang="EN-US">My setup
consists of 3 Raspberry Pis plugged into the local network behind my router.</span></div>
<div class="MsoNormal">
<span lang="EN-US">On the
router assigned static IP addresses and logical names to them. The Pis are
named “frodo”, “samwise” and “gandalf”. <o:p></o:p></span><br />
<span lang="EN-US">Naming your Pis according to your rooter's configuration will help you to stay on top of things later.</span><br />
<span lang="EN-US"><br /></span>
<span lang="EN-US">Before getting serious with playing JEE on the Pi we'll have to do some minor configurations on JBoss.</span><br />
<span lang="EN-US">But I leave this for the next post.</span></div>
Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com3tag:blogger.com,1999:blog-1625776533566626066.post-67383644657844963392013-08-07T21:18:00.001+02:002013-08-07T21:18:39.628+02:00Why this blog?<div class="MsoNormal">
<h4>
<span lang="EN-US">Why this
blog?</span></h4>
</div>
<div class="MsoNormal">
<span lang="EN-US">This blog
was inspired by a very interesting enterprise architecture project I had the
pleasure of being part of. Two reasons made this project somehow more
interesting than others. The architecture was supposed to cover service integration
spanning the cloud, data center hosted services and services running on small
and very small devices operated in the field. On top of that a lot of these
field devices were manufactured by the customer himself, making them quite
propriety.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">This
translates into distributed enterprise computing in a very heterogeneous
environment.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Perfect fit
for the by now quite mature JEE technologies? <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">In the end,
only time will tell. But what we’ve seen so far looks really promising.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">Anyway, we
used a lot of “heavy weight” JEE technologies on very limited devices. And we
are still experimenting.<o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">This blog
describes some of these efforts. <o:p></o:p></span></div>
<div class="MsoNormal">
<span lang="EN-US">I choose
the Raspberry Pi, or Pi for short, for representing our small field devices
because it is easily available and quite cheap. Two or three Pis in a local
network and JEE computing suddenly becomes very tangible and a lot of fun (if
you have the kind of humor for this).<o:p></o:p></span><br />
<span lang="EN-US"><br /></span></div>
<span lang="EN-US" style="font-family: "Calibri","sans-serif"; font-size: 11.0pt; line-height: 115%; mso-ansi-language: EN-US; mso-bidi-font-family: "Times New Roman"; mso-bidi-language: AR-SA; mso-fareast-font-family: Calibri; mso-fareast-language: EN-US;">If
you find this to be interesting, keep on reading and let me know what you
think.</span>Volker Kosterhttp://www.blogger.com/profile/07599015695285648692noreply@blogger.com0