First thoughts about Rust

Introduction

I wanted to try new programming language. A language that is trivial and complex at the same time. Trivial to write fast, complex when struggling with performance or when you want state of art architecture.

Baby steps

This week I decided to try Rust . Rust is an expression-based language. What this mean? In general expression is collection of symbols that jointly express a quantity (or simply expression produce at least one value). But there are also statements which may be smallest standalone elements of an programming language or in other words statements are building blocks of the program.
In Rust everything is an expression, but in general we have two kind of statements. First is an binding statement (for example statement declaration) and second is expression statement, which purpose is to turn any expression into statement (for example adding ; at the end of the line). Why I even write this? Look at this if example which is statement.

If expression
1
let y = if x == 5 { 10 } else { 15 }; // y: i32

Notice that there are no semicolons after 10 and 15. This means that those are expression, and ‘if’ is an expression to, which mean that you can assign if result to y.

The basic program

After reading the first part of the Rust Book I decided to write my first program. I decided to write quick sort. Here is the code.
Lessons learned:

  • Writing a program without IDE and relay only on docs and error codes its very valuable for your learning progress.
  • Cargo, which is Rust build system is intuitive. Out of the box test and benchmark support is another advantage.
  • Primitives like in C. Good when struggling with performance. Bad when want to write fast.
  • Pointers like in C. Dangerous, but powerful toy. In next section there is a few words about how Rust extended pointer that I knew from C.
  • I didn’t like the ‘newtype’, that let you create a new type that’s similar to another one. The concept is good, but after this example, I would say I won’t use it (in most of the cases), because cost of difficulty in extracting value, is not worth providing this kind of type safe:
1
2
3
let length = Inches(3);  
let Inches(integer_length) = length;
println!("In inches size {}", length);
  • I didn’t like that Rust has two main types of strings: &str and String.

Memory management

Rust is about performance. Many of abstractions are done at compile time. There is said that new programers are fighting with compiler. I can confirm that. Once you gain more experience, it is all becoming easy for you. I have to admit that I’ve similar situation with Scala.

Experience of learning my first programming language came to me when in book pass-reference-by-value was discussed.
Rust take pointers to the whole new level. There are mutable references, boxes and more.
But in fact pointers are an introduction to Rust memory aspect such as: Ownership, Borrowing, Boxes and Lifetimes.

I’ve great time playing around and checking what will work and what not. This is a recommended part of the book and this article for further reading.

For me it was good learning journey to read and try new Language. What will be next? Maybe Go or Haskell.

Photo credit: Rust Car, Rust Nut

Gerrit and maven release plugin

There is a time in your project when you start using Gerrit code review system. When you have maven release plugin to make release, you can be very surprised when you see:

1
2
3
[ERROR] The git-push command failed.  
[ERROR] Command output:
[ERROR] Permission denied (publickey).

This is a situation when we use ssh connections to gerrit. But, when you try to push something to master (ignoring code review), it works! How is that possible?

You probably have in your gitconfig an ssh URL with your user name. But in your project SCM (in pom.xml) you do not have your user name. What user name maven release plugin use? Your computer account name, which is in most cases different than your Gerrit user name.

How to repair it? Define a file in .ssh/config directory with content:

1
2
3
4
Host gerrit  
HostName YOUR_GERRIT_HOST
Port YOUR_GERRIT_PORT
User YOUR_GERRIT_USER_NAME

There may be a lot of other reason why you have Premission denied, but this was the hardest I’ve ever seen.

GNU sed and xpath on OS X

In my team, there is 20% of OS X machines, rest are linuxes. We share one script, that everyone on our team uses.

That script was written by some guy on Ubuntu and guess what? GNU versions of those programs (sed and xpath) are not compatible with BSD versions. The script was failing :(

I was trying to improve the script, but forget about it. Just use GNU programs.

To install gnu sed on osx via homebrew type the following:

GNU Sed
1
brew install gnu-sed --with-default-names

To install gnu xpath on osx via homebrew type the following:

GNU Xpath
1
2
3
4
brew tap concept-not-found/tap  
brew install xpath
mv /usr/local/bin/xpath /usr/local/bin/osx.xpath
sudo ln -s /usr/local/Cellar/xpath/1.13-7/bin/xpath /usr/local/bin/xpath

New methods in Map.Entry comparingByKey and comparingByValue

JDK 8 is still new and hot. I have explored some new methods in Map.Entry interface, to sort objects.

Image that we have following collection:

Initial data
1
2
3
4
5
Map map = new HashMap<>();
map.put("Kawasaki", 3);
map.put("Honda", 1);
map.put("Norton", 5);
map.put("Moto Guzzi", 2);

There are four new methods:

  • comparingByKey()
  • comparingByKey(Comparator<? super K> cmp)
  • comparingByValue()
  • comparingByValue(Comparator<? super K> cmp)

The method names are self-describing. If we pass different comparator, we can get different behaviour.

Comparing by value

If we want to sort by value with default behaviour (ascending), we can do something like this:

ComparingByValue
1
2
3
4
5
6
7
List<Map.Entry<String, Integer>> comparingByValue = map
.entrySet()
.stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toList());

comparingByValue.forEach(System.out::println);

As a result, we get a list sorted by values:

1
2
3
4
Honda=1
Moto Guzzi=2
Kawasaki=3
Norton=5

Comparing by key

If we want to sort by key with default behaviour (ascending), we can do something like this:

ComparingByKey
1
2
3
4
5
6
7
List<Map.Entry<String, Integer>> comparingByKey = map
.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toList());

comparingByKey.forEach(System.out::println);

As a result, we get a list sorted by keys alphabetically:

1
2
3
4
Honda=1
Kawasaki=3
Moto Guzzi=2
Norton=5

Comparing by key with comparator

If we want to sort by key length, we can pass function as a comparator,  Then we can achieve something like this:

ComparingByValue with comparator
1
2
3
4
5
6
7
List<Map.Entry<String, Integer>> comparingByValue = map
.entrySet()
.stream()
.sorted(Map.Entry.comparingByKey((String s1, String s2) -> s1.length() - s2.length()))
.collect(Collectors.toList());

comparingByValue.forEach(System.out::println);

As a result, we get a list sorted by keys length:

1
2
3
4
Honda=1
Norton=5
Kawasaki=3
Moto Guzzi=2

Photo credit

Listing files in Git

Exploring git ls-files

This post describes, my experiments with git ls-files command.

In repository, we have files:

File structure in repository
1
2
3
4
5
6
7
.DS_Store (Mac OS X, folder settings file)
.git (Git repository file)
.gitignore (Git ignore file)
ignoredFile (Some file, that should be ignored)
inRepo (File in repository)
untracked (Som untracked file)
staged (Staged file)

This command show us, all committed files and staged files.

List files
1
2
3
4
$ git ls-files
.gitignore
inRepo
staged

This command show us, all files that are ignored or untracked. Those files are called ‘other’.

List ignored or untracked
1
2
3
4
$ git ls-files --others
.DS_Store
ignoredFile
untracked

This command show us, all others files, without ignored files. Option exclude-standard means that standard git exclusion files are included.

List others files, without ignored files
1
2
$ git ls-files --others --exclude-standard
untracked

This command show us, all files that are ignored.

List all ignored files
1
2
3
$ git ls-files --ignored --others --exclude-standard
.DS_Store
ignoredFile

In my global git ignore file, there is a rule to ignore all .DS_Store files. In my local git ignore file, there is a rule to ignore ignoredFile file.

Trigger Jenkins build from BitBucket

W tym poście przedstawię jak zrobić, aby natychmiast po wypchnięciu zmian do BitBucket.org serwer ciągłej integracji rozpoczął proces budowania aplikacji.

Konfiguracja Jenkinsa

  • W związku z tym, że nasz Jenkins musi być dostępny publicznie, warto go trochę zabezpieczyć. Dostęp do Jenkinsa ograniczymy tylko dla zarejestrowanych użytkowników. Opcje konfiguracji zabezpieczeń powinny być ustawione tak jak na screenie poniżej.

 

Zabezpieczenie

  • Nazwa użytkownika w tym przypadku to “lewy”. Następnie musimy pobrać nasz “user API token”. Znajdziemy go tutaj:

API Token

  • Następnie musimy włączyć dla naszego projektu, możliwość zdalnego triggerowania budowania:

Zdalne trigerowanie

  • Aby przetestować konfigurację Jenkinsa wywołaj zapytanie do serwisu:

http://lewy:USER_KEY@klkl.pl:8081/job/WorkTracker/build?token=PROJECT_KEY

Po wykonaniu tej operacji, nasz serwer Jenkinsa powinien ruszyć z nowym procesem budowania.

Konfiguracja BitBucketa

BitBucket’a konfigurujemy tak jak na screenie przedstawionym poniżej:

Konfiguracja BitBucket

Jeżeli tak skonfigurujemy nasze środowisko, po pchnięciu zmian, serwer ciągłej integracji, powinien automatycznie rozpocząć budowanie aplikacji. 

Niestety BitBucket nie udostępnia żadnych logów, więc jeżeli coś nie działa, to pozostaje nam próbować zmieniać coś w ciemno, aż zadziała.

Replace with Regex

Ostatnio przechodziłem z HibernateSessionFactory konfigurowanego w kodzie na hibernate.cfg.xml.
Wiąże się to z zamianą mapowań, których może być w dość dużo w projekcie:

Code Configuration
1
configuration.addAnnotatedClass(pl.myproject.MyClass.class);

Każda klasa reprezentująca encje musi zostać zamieniona na coś takiego:

Code Configuration
1
<mapping class="pl.myproject.MyClass">

Najprostsze rozwiązanie jakie przychodzi to użycie zamiany z użyciem wyrażenia reguralnego. W naszych IDE należy włączyć tryb zamiany z użyciem wyrażenia reguralnego. W polu “find” definiujemy:

Code Configuration
1
configuration.addAnnotatedClass\((.*).class\);

W polu “replace” definiujemy:

Code Configuration
1
<mapping class="$1"></mapping>

Klikamy “Replace All” i zrobione. Nie ma tutaj nic trudnego. Wybranie pierwszego podzbioru za pomocą dwóch nawiasów i wstawienie tego za pomocą $1.
Proste,a nawet banalne.
Za pewne każdy edytor tekstu potrafi takie rzeczy, jednak odnoszę wrażenie, że zbyt często próbujemy robić takie rzeczy ręcznie.

Photo credit

Tomcat cluster with mod_jk sticky session session replication

Przewodnik o tym jak zrobić klaster złożony z dwóch instacji tomcata połączonego load balancerem apache mod_jk i jak uruchomić replikację sesji oraz sticky-session. System operacyjny to lubuntu 11.10.

Z góry przepraszam, za dość surowe komendy. Nie jest to tutorial krok-po-korku ani książka, żeby rozlegle wszystko opisywać. Jest to tylko ściągawka na przyszłość, lub ekstrakt z tego co trzeba zrobić.

Adres IP należy zmienić z 10.0.0.90 na localhost czy gdzie tam wasz serwerek się znajduje.

Instalacja

  • Zainstaluj:

    Instalacja
    1
    apt-get install apache2 libapache2-mod-jk2-mod-jk
  • Utworz katalog:

    Folder
    1
    /tcluster/instance1
  • Pobierz Tomcata i rozpakuj zawartość do tego katalogu.

Konfiguracja Apache mod_jk

  • Utwórz plik /etc/apache2/workers.properties:
Worker properties time
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
worker.list=loadbalancer,mystat

worker.worker1.type=ajp13
worker.worker1.host=10.0.0.90
worker.worker1.port=8881
worker.worker1.lbfactor=50
worker.worker1.cachesize=10
worker.worker1.cache_timeout=600
worker.worker1.socket_keepalive=1
worker.worker1.socket_timeout=300

worker.worker2.type=ajp13
worker.worker2.host=10.0.0.90
worker.worker2.port=8882
worker.worker2.lbfactor=50
worker.worker2.cachesize=10
worker.worker2.cache_timeout=600
worker.worker2.socket_keepalive=1
worker.worker2.socket_timeout=300

worker.loadbalancer.type=lb
worker.loadbalancer.sticky_session=true
worker.loadbalancer.balance_workers=worker1,worker2

worker.mystat.type=status

Ważna jest nazwa workera ( worker1 oraz worker2 ) adres IP oraz port.

  • Do pliku /etc/apache2/ports.conf dodaj:
    Listen port time
    1
    Listen 8585

Zmień zawartość pliku /etc/apache2/mods-enabled/jk.conf na:

Mods configuration
1
2
3
4
5
6
JKWorkersFile /etc/apache2/workers.properties
JkLogFile /var/log/apache2/mod_jk.log
JkLogLevel debug
JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
JkRequestLogFormat "%w %V %T"

Dodaj nasłuch na porcie 8585 dla mod_jk. Zmień plik /etc/apache2/sites-enabled/000-default

1
2
3
4
<VirtualHost *:8585>
JkMount /* loadbalancer
JkMount /jkstatus mystat
</VirtualHost>

Konfiguracja instancji nr 1:

Zmień plik /tcluster/instance1/conf/server.xml tak aby przypominał:

Konfiguracja Tomcata 1
1
2
3
4
5
6
7
8
...
<Connector port="8871" protocol="HTTP/1.1" />
...
<Connector port="8881" protocol="AJP/1.3" redirectPort="8443" />
....
<Engine name="Catalina" defaultHost="localhost" jvmRoute="worker1">
....
<Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>

Connectora się odkomentowywuje, jvmRoute trzeba dopisać, pole cluster się odkomentowywuje.

  • Ściągnij z PSI Probe i umieść go w katalogu webapps. Jest to aplikacja która może się później przydać.

  • Dodaj użytkowników do tomcata. Zedytuj plik /tcluster/instance1/conf/tomcat-users.xml:

    Tomcat users
    1
    2
    3
    4
    5
    <tomcat-users>
    ...
    <role rolename="manager"/>
    <user username="admin" password="jakieshaslo" roles="manager"/>
    </tomcat-users>
  • Przejdź do katalogu: /tcluster/instance1/webapps.
    Zmodyfikuj _examples/WEB-INF/web.xml_ dodając distributable:

    Distributable
    1
    2
    3
    4
    5
    <tomcat-users>
    ...
    <role rolename="manager"/>
    <user username="admin" password="jakieshaslo" roles="manager"/>
    </tomcat-users>

Konfiguracja instancji nr 2:

  • Skopiuj zawartość pliku /tcluster/instance1 do /tcluster/instance2
  • Zmień plik /tcluster/instance2/conf/server.xml tak aby zmienić:
Konfiguracja Tomcat 2
1
2
3
4
5
6
7
<Server port="8006" shutdown="SHUTDOWN">
...
<Connector port="8872" protocol="HTTP/1.1" />
...
<Connector port="8882" protocol="AJP/1.3" redirectPort="8443" />
....
<Engine name="Catalina" defaultHost="localhost" jvmRoute="worker2">

Testowanie

Uruchamiamy wszystko:

Startup
1
2
3
/etc/init.d/apache start
/tcluster/instance1/bin/startup.sh
/tcluster/instance2/bin/startup.sh

Testy przeprowadzić najlepiej za pomocą aplikacji z przykładów:

W moim przypadku jest ona dostępna pod adresem: http://10.0.0.90:8871/examples/servlets/servlet/SessionExample

Mamy trzy porty HTTP:

  • 8871 - instancja nr 1
  • 8872 - instancja nr 2
  • 8585 - load balancer

Po dodaniu jakiegoś aktrybutu sesji i zmianie portu z 8871 na 8872 powinniśmy mieć ten sam SessionID (Sticky-session) oraz te same atrybuty(Replikacja sesji).

Dodatkowa analiza:

  • Panel z informacja o mod_jk dostępny pod adresem http://10.0.0.90:8585/jkstatus
  • Probe (narzędzie do monitorowania) dostępne na danej instancji: http://10.0.0.90:8871/probe oraz http://10.0.0.90:8872/probe

Photo credit