Archive for Listopad, 2008


Programowanie w parach

Niniejszym postem chciałbym zapoczątkować serię wpisów mających na celu przybliżenie tematyki wytwarzania oprogramowania w metodyce Agile. Jako osoba zainteresowana tematyką chciałbym przybliżyć jej stosowanie, przy okazji zdobywając wiedzę.

Od jakiegoś czasu zastanawiam się jak to jest programować w parach. Nie miałem do tej pory takiej możliwości z w powodu polityki w firmie, co nie zmienia faktu, że byłoby to ciekawe wyzwanie. Pragnę umieścić tutaj teoretyczne rozważania na temat pair programming.

Czym jest programowanie w parach?

Programowanie w parach to praca 2 osób przy jednej klawiaturze. Może się wydawać, że produktywność takiego tandemu jest równa ilości kodu która jest w stanie napisać jedna osoba. Po pierwsze – produktywność nie można liczyć w ilościach linii kodu! Jeżeli ktoś sądzi, że jest inaczej – czekam na komentarz. Pisanie oprogramowania polega głównie na myśleniu, a dopiero potem należy skonwertować myśli do wybranego języka programowania. A więc dlaczego by nie dorzucić drugiej osoby, która odpowiedzialna by była za bycie o jeden krok przed osobą przelewającą ustalenia na kod? Na tym polega programowanie w parach. Reasumując – programowanie w parach polega na podziale ról pomiędzy uczestników – jedna osoba prowadzi, druga jest pilotem. Osoba prowadząca siedzi przy klawiaturze i pisze kod. Rolą pilota jest myślenie o następnych zadaniach, możliwych błędach oraz śledzenie kodu pisanego przez prowadzącego.

Wiemy już jak wygląda rozkład ról, ale co z tą efektywnością?

Programowanie w parach wymusza najwyższą wydajność obu programistów. Celowo użyłem słowa wymusza, ponieważ nie wyobrażam sobie, aby ktoś z pary mógł zacząć myśleć o czymś nie związanym z aktualnie rozwiązywanym problemem, gdzie w każdej z chwili może zostać poproszony o komentarz, czy radę.

Prawdą jest, że na stworzenie takiej samej ilości kodu przez parę, w porównaniu z pojedynczą osobą, należy poświęcić więcej czasu. To jest oczywista oczywistość jakby powiedział pewien mentor IV RP. Należy od razu dodać, że kod wytworzony przez parę jest bardziej przemyślany, a co za tym idzie – wyższej jakości. Należy sobie odpowiedzieć na pytanie czy chcemy mieć aplikacją napisaną szybko, czy dobrze? Zdaję sobie sprawę, że jest to pewnego rodzaju uogólnienie, jednak odpowiedź pozostawiam czytelnikom.

Tworzenie par i rotacje

Reguły dotyczące tworzenia par są bliźniacze z tymi, które obowiązują w normalnym życiu. Jeżeli osoby tworzące parę nie są ze sobą  zgrane – współpraca nie będzie przebiegała prawidłowo lub wręcz nie będzie możliwa. Każda osoba jest indywidualnością. Nie należy w żadnych przypadku naciskać, aby konkretne osoby stworzyły parę. Tworzenie pary powinno odbywać się na drodze selekcji naturalnej osób zainteresowanych.

Programowanie w parze

źródło: http://www.wikihow.com/Image:Pairprogramming_996.jpg, licencja: http://creativecommons.org/licenses/by/2.5/

Osoby w parze zamieniają się miejscami co jakiś czas. Niektórzy zalecają zamianę po 90 minutach twierdząc, że to najlepszy czas. Dobrym wyjście jest również zamiana wtedy, gdy pilot chce coś pokazać lub po prostu ma pomysł na rozwiązanie problemu. Zamiana może również mieć miejsce po wykonaniu sekwencji zadań, np. napisanie kodu do wcześniejszego testu, oraz napisanie kolejnego testu, tak aby osoba, która usiądzie przy klawiaturze musiała napisać kod do tego testu.

Należy pamiętać, że partner nie jest przypisany “raz na zawsze”. Rotacja jest mile widziana, a nawet zalecana. W związku z czym nasuwa się pytanie – kiedy zmieniać partnera? Najprostszą odpowiedzią jest: kiedy zajdzie taka potrzeba! Przykładem może być dłuższe zastanawianie się nad konkretnym problemem. Często jest tak, że świeży umysł zauważy wyjście z problemu dużo szybciej. Drugim ważnym aspektem zmian w parach jest to, że cały zespół wie co się dzieje w systemie, w każdym jego miejscu – mówimy o tzw. współwłasności kodu.

Ergonomiczne miejsce pracy

Dwie osoby to nie jedna. Do programowania w parach potrzebne jest miejsce. Zależy oczywiście od ustaleń między programistami, ale na ogół potrzeba dużo przestrzeni, aby zespół mógł dobrze współpracować. Swoją drogą – cała metodologia Agile wymaga odpowiednich warunków pracy, o czym będzie w innym poście.

Należy wystrzegać się takiego ułożenia, że jedna osoba jest za plecami drugiej. To nie wchodzi w rachubę z powodu czysto psychologicznych spraw. Osoba prowadząca w takim przypadku jest w ciągłym stresie, co nie sprzyja produktywności.

Biurko powinno być odpowiednio duże aby dwie osoby mogły wygodnie siedzieć obok siebie. Monitor powinien być na tyle duży, aby obie osoby miały komfortowy wgląd w kod. Dobrym rozwiązaniem są dwa monitory.

Osobowości i różnice w umiejętnościach

Nie wszyscy programiści są najwyższej klasy specjalistami. Idealnym rozwiązaniem byłoby, gdyby tak było faktycznie, ale niestety nie jest. W takim przypadku należy znaleźć taki element aplikacji, gdzie osoba mniej potrafiąca będzie w stanie szybkim czasie nadrobić zaległości i stać się ekspertem w danym obszarze.

W programowaniu w parach trzeba być delikatnym. Uwagi zwracać konstruktywne i to na ogół w formie pytań, np.: Nie sądzisz, że warto by było wydzielić ten blok do osobnej metody? Kwintesencja grzeczności, kultury, a zarazem dobra sugestia.

Nie należy się obrażać. Być może łatwiej powiedzieć to, niż wcielić w życie, ale w trakcie programowania w parach należy umieć dochodzić do kompromisów – zawsze. Jeżeli charakter danej osoby nie pozwala na prowadzenie programowania w parach należy dokonać rotacji.

Kiedy nie stosować programowania w parach?

Może się wydawać, że programowanie w parach to remedium na wszystkie bolączki wytwarzania oprogramowania. Tak oczywiście nie jest. Jeżeli przykładowo nie zapewnimy odpowiedniego miejsca pracy – programowanie w parach nie przyniesie żadnych korzyści. Jeszcze jedna ważna uwaga – jeżeli programiści mają zmienić dotychczasową organizację pracy na programowanie w parach pod przymusem, to murowanym skutkiem wprowadzenia w życie tej decyzji będzie niepowodzenie, czego skutkiem będzie spadek wydajności. Ewentualne zastosowanie metody programowania w parach powinno być decyzją zespołu, oczywiście po uzgodnieniu z władzami firmy.

Nie należy stosować programowania w parach w zespołach nie znajdujących się w jednym pomieszczeniu. Co prawda są dostępne możliwości techniczne takie jak zdalny pulpit, czy telefonia VOIP, jednak nic nie zastąpi werbalnego kontaktu będącego podstawą programowania w parach.

Podsumowanie

Programowanie w parach jest z pewnością dużym wyzwaniem dla programistów. Jednocześnie jest to praca męcząca, ale dająca zadowolenie, kod wytwarzanego oprogramowania jest lepszej jakości – zawiera mniej błędów. Samo oprogramowanie jest dużo łatwiejsze w konserwacji z uwagi na współwłasność kodu, a dług techniczny (o którym w innym poście) maleje.

Osoby programujące w parach są maksymalnie skoncentrowane na zadaniu. Rozproszenia zgranej pary jest trudne, ponieważ osoby te są pochłonięte pracą, co jest dużą korzyścią dla wszystkich – programistów z uwagi na zdrowe zmęczenie i tworzącą się więź między osobami w zespole, jak i dla firmy – z uwagi na efekty w postaci dynamicznego rozwoju aplikacji posiadającej mało błędów.

Zachęcam do komentowania! Chciałbym usłyszeć Wasze opinie i sugestie na ten temat.

Postanowiłem zmigrować moją aplikację służącą jako poligon na nową bazę danych. Dlaczego? Powód trywialny – chcę nabyć doświadczenie przy administracji inną bazą danych. Poza tym, podobno PostgreSQL jest całkiem dobrą bazą :) Niestety nie miałem okazji przyjrzeć jej się z bliska – czas to zmienić!

Zainstalowałem PostgreSQL w najnowszej wersji 8.3.5. Super, bazę już mam. Teraz trzeba się do niej podłączyć. Tylko jak? Google powiedziało, że jest graficzny interfejs zwiący się pgAdmin. Ściągnąłem, uruchomiłem i… No tak a jaki użytkownik i hasło? Przeglądając dokumentację trafiłem na komentarz który naświetlił mi sprawę zarządzania użytkownikami.

Po założeniu odpowiedniego użytkownika i bazy danych postanowiłem się podłączyć za pomocą wcześniej wspomnianego pgAdmina. Po kilkukrotnych próbach wpisywania loginu i hasła, które za każdym razem były odrzucane przez serwer – dałem sobie spokój.

Znów kolej na dokumentację. Dowiedziałem się dzięki niej, że jest odpowiednik komendy mysql: psql. No to uruchamiam psql – zalogowałem się. Zdziwiłem się, że nie musiałem podawać hasła. Wykonałem parę przykładowych zapytań – działa. Hmm, tylko dlaczego nie działało z pgAdminem? No nic – spróbuję zmigrować aplikację.

Pobrałem najnowszy sterownik JDBC i umieściłem go w katalogu z bibliotekami serwera JBoss ($JBOSS_HOME/server/[konfiguracja]/lib). Zmieniam dialekt Hibernate’a oraz plik *-ds.xml w którym znajdują się informacje w jaki sposób serwer ma połączyć się z bazą danych. Uruchamiam serwer – a może się uda :)

Nie, nie tym razem… Autoryzacja użytkownika nie przebiegła pomyślnie – otrzymałem komunikat: IDENT authentication failed for user "system". Długo się zastanawiałem o co chodziło z tym słówkiem IDENT, aż trafiłem na wpis w blogu który mnie oświecił :) Okazało się, że domyślnym sposobem autoryzacji jest IDENT, co oznacza autoryzację użytkowników za pomocą protokołu identyfikacji (RFC 1413). Przy logowaniu lokalnych użytkowników za pomocą gniazd, baza danych autoryzuje użytkowników automatycznie. I taka jest odpowiedź na zagadkę dlacego nie musiałem podawać hasła chcąc się dostać do psql.

Aby umożliwić logowanie za pomocą hasła należy wyedytować plik /var/lib/pgsql/data/pg_hba.conf zamieniając każde wystąpienie słowa ident na md5. Po aktualizacji należy ponownie uruchomić serwer bazy danych.

Ponowiłem próbę uruchomienia aplikacji – tym razem błędy wystąpiły przy eksporcie schematu bazy danych. Okazało się, że słowo user jest zastrzeżonym słowem kluczowym w PostgreSQL. Szybka zmiana parametru name adnotacji @javax.persistence.Column przy odpowiednich polach pomogła.

Kolejnym problem unaocznił się podczas importu przykłądowych danych. PostgreSQL używa sekwencji! Musiałem zmienić sposób generowania kluczy głównych właśnie na sekwencje. Na szczęście JPA pozwala to w dosyć prosty sposób zrobić za pomocą adnotacji @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.SEQUENCE), co nie zmienia faktu, że sama zmiana w wielu plikach zajęła mi trochę czasu.

Po kolejnym uruchomieniu okazało się, że zapomniałem przystosować przykładowe dane, które były importowane przy każdym uruchomieniu aplikacji na serwerze, do obsługi sekwencji. Po tej czynności aplikacja uruchomiła się poprawnie.

Podsumowanie

Czas stracony na szukaniu rozwiązań: 3 godziny, wiedza zdobyta przy okazji: bezcenna :)

Uczestniczyłem wczoraj w drugim spotkaniu Silesia JUG. Tematem przewodnim była Platforma Netbeans a prelegentem był Marek Kliś. Co prawda temat średnio mnie interesujący, ale wybrałem się, ponieważ miałem ostatnio do czynienia z aplikacjami biurkowymi pisanymi w Javie, a nie było to przeżycie należące do tych, które warto pamiętać…

Temat został przedstawiony dosyć pobieżnie. Największym problemem była chyba próba pisania aplikacji “od zera”. Dopiero pod koniec Marek zaczął kopiować kod z przykładu napisanego wcześniej, co pozwoliło na skupienie się na sednie sprawy. Zabrakło mi podsumowania. Dlaczego warto tego używać? Co nam to daje, w czym może nas wyręczyć? Chętnie posłuchałbym jeszcze o platformie, jednak już ze skupieniem się na meritum. Tak czy owak – chciałbym podziękować Markowi za prezentację i przeprosić go za serię moich pytań. Pod koniec chyba był już zdenerwowany z tego powodu :)

Frekwencja (ok. 20 osób) tym razem była dużo niższa niż ostatnio. Śląsk, duża aglomeracja, a nie ma osób zainteresowanych technologiami Javy? Coś jest nie tak, może nie ten temat? Może za mało promocji? Zobaczymy na następnym spotkaniu – które odbędzie się już za tydzień! Będzie mowa o Mule ESB, a prelegentem będzie założyciel grupy – Łukasz Lipka. Zapowiada się ciekawie, szczególnie jeżeli wziąć pod uwagę zapowiedzi głębszego spenetrowania tematu.

W skrócie:

JBoss Seam w swojej najnowszej odsłonie (2.1.0.SP1) w porównaniu z wersją 2.0 posiada bardzo wiele zmian związanych z bezpieczeństwem. Omówione one zostały na przykładzie aplikacji SeamSpace w poście na blogu Shane’a Bryzaka – głównego deweolpera kwestii bezpieczeństwa w JBoss Seam. Osobom zainteresowanym polecam również przestudiować rozdział poświęcony bezpieczeństwu w oficjalnej dokumentacji Seama.

Nowością w Seam 2.1 jest możliwość definiowania uprawnień i przypisywania ich określonym rolom. Jest to rozwiązanie bardzo elastyczne, jak również bezpieczne, a co najważniejsze – wygodne! Postanowiłem sprawdzić czy sprawdza się to równiez w praktyce.

W swojej aplikacji postawiłem wykorzystać JpaPermissionStore, jak również JpaIdentityStore aby móc zapisywać uprawnienia i użytkowników w relacyjnej bazie danych. Po odpowiedniej konfiguracji (więcej info w poście na blogu Shane’a) oraz zabezpieczeniu odpowiednich stron okazało się, że każdorazowo (sic!) gdy jest sprawdzane jakieś uprawnienie – wykonywane jest zapytanie do bazy danych! W przypadku wielokrotnego sprawdzania uprawnień na jednej stronie (a ma to miejsce praktycznie zawsze) dochodziło do wykonywania nawet kilkudziesięciu zapytań – bardzo często takich samych.

Oczywiście trzeba było to zneutralizować. Jak? Nadpisując komponent org.jboss.seam.security.permission.PersistentPermissionResolver w ten sposób:

package pl.atlone.firma.auth;

import java.util.HashMap;
import java.util.Map;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Observer;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Startup;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.log.Log;
import org.jboss.seam.security.Identity;
import org.jboss.seam.security.permission.PersistentPermissionResolver;

@Scope(ScopeType.APPLICATION)
@BypassInterceptors
@Name("org.jboss.seam.security.persistentPermissionResolver")
@Install(precedence = Install.APPLICATION)
@Startup
public class SystemPermissionResolver extends PersistentPermissionResolver {

	private static final long serialVersionUID = -9063212676529968783L;

	@Logger
	private Log log;

	private Map<string , Map<String, String>> permissions = new HashMap</string><string , Map<String, String>>();

	@Observer( { "System.Permission.Removed", "System.Permission.Updated" })
	public void onSecurityUpdate() {
		permissions.clear();
	}

	@Override
	public boolean hasPermission(Object target, String action) {

		log.trace("Checking permissions: [identity = "
				+ Identity.instance().getCredentials().getUsername()
				+ "], [target = " + target + "]. [action = " + action + "]");

		Map</string><string , String> identityPermissions = permissions.get(Identity
				.instance().getCredentials().getUsername());

		if (identityPermissions == null) {
			identityPermissions = new HashMap</string><string , String>();
			permissions.put(Identity.instance().getCredentials().getUsername(),
					identityPermissions);
		}

		String permission = identityPermissions.get(target.toString());

		if (permission != null && permission.equals(action)) {
			return true;
		} else {
			boolean hasPermission = super.hasPermission(target, action);

			if (hasPermission) {
				identityPermissions.put(target.toString(), action);
				return true;
			}
		}

		return false;
	}
}

Otrzymujemy w ten sposób komponent pobierający informacje z bazy danych tylko wtedy, gdy żądana informacja nie została już wcześniej pobrana. Komponent bardzo prosty i przyjemny. Należy pamiętać, aby po usunięciu lub uaktualnieniu uprawnienia wywołać odpowiednio zdarzenia System.Permission.Removed lub System.Permission.Removed, co spowoduje wyczyszczenie listy uprawnień znajdujących się w pamięci, a zarazem wymusi załadowanie nowych, uaktualnionych uprawnień.

Aby scacheować encję w serwerze JBoss należy opatrzyć ją adnotacją @org.hibernate.annotations.Cache:

import javax.persistence.Entity;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

@Entity
@Table(name = "role_permissions")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class RolePermission extends BaseEntity {

	private static final long serialVersionUID = 6778357970340362708L;

	private String role;
	private String target;

...

}

oraz dodać odpowiednie właściwości do pliku persistence.xml:

<property name="hibernate.cache.provider_class" value="org.jboss.ejb3.entity.TreeCacheProviderHook" />
<property name="treecache.mbean.object_name" value="jboss.cache:service=EJB3EntityTreeCache" />

Dodatkowo należy posiadać odpowiednio skonfigurowany serwer. Są na to dwa sposoby:

  1. Uruchomienie serwera w konfiguracji all,
  2. Uruchomienie serwera w konfiguracji default po jej uprzednim przygotowaniu.

Drugi sposób jest o tyle lepszy, że jest start serwera jest zdecydowanie szybszy, co na maszynie deweloperskiej jest dużym atutem. Konfiguracja all pozwala na klastrowanie, ale komu to potrzebne przy tworzeniu i testowaniu aplikacji?

Aby uruchomić cacheowanie encji przy konfiguracji default (domyślnej) należy skopiować z katalogu $JBOSS_HOME/server/all/lib/ biblioteki jboss-cache-jdk50.jar oraz jgroups.jar do katalogu $JBOSS_HOME/default/lib/.

Dodatkowo należy skopiować plik $JBOSS_HOME/server/all/deploy/ejb3-entity-cache-service.xml do katalogu $JBOSS_HOME/default/deploy/.

I to wszystko – możemy już cieszyć się encjami z pamięci podręcznej.

Poprawność uruchomienia cacheowania można sprawdzić w konsoli JMX serwera JBoss pod adresem http://localhost:8080/jmx-console/ wyszukując usługę *:service=EJB3EntityTreeCache,*, a następnie uruchamiająć akcję printDetails(). Na wynikowym ekranie powinna się znaleźć encja oznaczona adnotacją @Cache.

Follow

Otrzymuj każdy nowy wpis na swoją skrzynkę e-mail.