GNU sed and xpath on OS X

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

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

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.

Fest Assert
Trigger Jenkins build from BitBucket

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

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

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

Count number of lines in project