Mariusz Prowaźnik

o programowaniu w Javie, Scali i Clojure.


Jak wyzwolić kreatywność?

Do napisania tego postu zainspirował mnie artykuł "Arts coaching w służbie biznesu" z Business Coaching nr4/2010, który opisuje wykorzystanie pracy artystycznej w szkoleniach biznesowych. Nie będę oczywiście go streszczać, chciałbym się po prostu zastanowić na temat tego, jak można wyzwolić więcej kreatywności w pracy programisty.

Ta praca może dawać ogromne możliwości tworzenia, realizowania wizji nowoczesnego systemu, może też być tylko nudnym klepaniem kodu, łataniem dziur w podstarzałych aplikacjach. Programista może być ograniczony przez wiele zewnętrznych czynników, ale najważniejszym czynnikiem w tym procesie jest on sam, jego podejście i sposób myślenia, bo to człowiek jest źródłem kreatywności, dzięki której pokonuje lub obchodzi się ograniczenia i dzięki kreatywności praca staje się ciekawa i fascynująca.

Jak zatem tę kreatywność w sobie wyzwolić?

Nie piszę poradnika, chcę się tylko podzielić kilkoma spostrzeżeniami na ten temat, oraz myślami zaczerpniętymi z różnych innych źródeł. Dlatego jeśli z czymś się nie zgadzacie, macie inne spostrzeżenia, to zachęcam do komentowania.

Odpoczywając. Nie raz mi się przydarzyło zmarnować godziny na problem, który następnego dnia, po odpowiednim wypoczynku rozwiązałem w 5 minut. Zmęczenie i stres nie sprzyja pomysłowości. Nie bez powodu coraz częściej można się spotkać z tym, że programiści w pracy mają do dyspozycji "relax/chillout room". Dlatego warto zadbać, żeby pracę rozpoczynać wypoczęty.

Odmulając się. Na szkoleniach BHP zwykle mówią, by co godzinę robić pięciominutową przerwę, by podczas niej robić ćwiczenia rozciągające itp... Jak jest w praktyce? Programista siedzi i siedzi, klepie i klepie. Brak ruchu prowadzi do rutyny, rutyna prowadzi do automatyzmu, a automatyzm prowadzi do ... ciemnej strony mocy. Póki co programowanie nie jest jak praca przy taśmie, więc krytyczne i twórcze myślenie może się przydać. By je rozruszać, czasem wystarczy rozruszać siebie.

Pytając. Siebie i innych. Szukając inspiracji u innych, pytając jak radzą sobie ze swoimi zadaniami i próbując dla odmiany ich metody. Zastanawiając się, nad powodami stosowania takiego a nie innego stylu pracy, jakie są za i przeciw. Zidentyfikować przyjęte założenia i spytać siebie, czy na pewno są prawdziwe? Co można zrobić lepiej? Czy można zaryzykować, sprawdzić jakąś nową metodę, nowe narzędzia?

Robiąc burzę mózgów, tworząc mapy myśli. Zapewne większość z nas kiedyś uczestniczyła w burzy mózgów i ma jakieś wyobrażenie na ten temat. Nie wiem jak inni, ale ja dosyć długo nie zdawałem z istotnego elementu tego narzędzia. Zacytuję książkę Christine Ingham, Automotywacja na 101 sposobów:
Dyskusja i komentarze na temat względnych korzyści czy skuteczności każdego z nich odbywają się dopiero pod koniec sesji. Jej celem jest uzyskanie jak największej liczby pomysłów, bez względu na to, jak dziwaczne czy niepraktyczne mogą one się wydawać.

Istotne jest wyraźnie rozdzielenie fazy generowania pomysłów, od fazy oceniania ich. Przechodząc zbyt szybko do krytyki, można zablokować napływ wielu dobrych pomysłów, które często ewoluują z kiepskich. Z podobnym podejściem spotkałem się w programie Briana Tracy'ego o zarządzaniu czasem (link). Zalecał, by po określeniu problemu, wymyślić 20 rozwiązań. By pójść na ilość. Dopiero potem, wybrać jedno, najlepsze i je zastosować. W podobny sposób można użyć map myśli. Przed rozpoczęciem pracy, poświęcić chwilę na wyrysowanie swoich zadań, skojarzeń, wszystkiego co w głowie siedzi. Potem spojrzeć na to i wybrać jedną, najważniejszą, rzecz, której zrobienie będzie najlepszym wykorzystaniem posiadanego czasu.

Tworzyć sztukę. Artysta, w odróżnieniu od rzemieślnika, tworzy coś co stanowi wartość estetyczną, nie użytkową. Nie chodzi o cel, o to czy powstanie jakiś produkt, ważne dla niego jest by zaangażować się w nieskrepowanym procesie twórczym, wyrażając siebie i swoje emocje. Oczywiście kod musi być użyteczny, programowanie nie jest dziedziną sztuki. Ale poświęcając się od czasu do czasu czemuś co jest dziedziną sztuki, może zaowocować pobudzeniem nieużywanych dotąd form myślenia i postrzegania.

Przy tym wszystkim warto zdawać sobie sprawę, że w kreatywności nie chodzi o recepty, jest ona przecież umiejętnością działania bez nich. Chodzi o inspirację i o to by być gotowym dostrzegać ją wokół. By pozwolić pomysłom rozkwitać, przeobrażać się i dać zaskakiwać się tym procesem.


Konfiguracja projektu z profilami Mavena i Springiem

W poście tym pokażę, jak przy użyciu Spring'a i profili Maven'a można uporządkować sobie konfigurację projektu, która jest zależna od środowiska na które jest wdrażany projekt. Oto przykładowe zastosowania:

Problem 1
Projekt korzysta konfigurowalnych zasobów, np z bazy danych. W pliku kontekstu Springa określiłem potrzebne namiary: url, port, użytkownika hasło. Chciałbym mieć to jednak w oddzielnym pliku, żeby w razie potrzeby te namiary zmienić, bez przeglądania dużego xml'a z konfiguracją.

Rozwiązanie
W tym celu można użyć PropertyPlaceholderConfigurer, dzięki któremu odpowiednie namiary pobrane zostaną z oddzielnego pliku. W pliku kontekstu określa się lokalizację pliku z właściwościami, zawierającego pary klucz=wartość.
context.xml
<context:property-placeholder location="classpath:jdbc.properties"/>

<bean id="dataSource" destroy-method="close"
    class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName" value="${jdbc.driverClassName}"/>
  <property name="url" value="${jdbc.url}"/>
  <property name="username" value="${jdbc.username}"/>
  <property name="password" value="${jdbc.password}"/>
</bean>
jdbc.properties
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root

Problem 2
Aplikacja, którą projektuję jest wdrażana na różnych środowiskach (np. na testowym i deweloperskim) i na każdym z nich łączy się z inną bazą danych. Nie chcę za każdym razem ręcznie aktualizować właściwości źródła danych.

Rozwiązanie
Pewne właściwości można umieścić w pliku pom.xml w sekcji properties. Włączenie filtrowania zasobów przez Maven'a, poprzez podanie ścieżki do katalogu z zasobami w sekcji resource spowoduje, że, jeśli któryś z plików w zasobach zawiera klucze takie jak ${jdbc.password}, to zostaną one zastąpione przez wartości właściwości z pliku pom.xml. W poniższym przykładzie podałem katalog src/main/resources, w którym znajduje się plik kontekstu.
context.xml
<bean id="dataSource" destroy-method="close"
    class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName" value="${jdbc.driverClassName}"/>
  <property name="url" value="${jdbc.url}"/>
  <property name="username" value="${jdbc.username}"/>
  <property name="password" value="${jdbc.password}"/>
</bean>

pom.xml
<properties>
  <jdbc.driverClassName>org.hsqldb.jdbcDriver</jdbc.driverClassName>
  <jdbc.username>user</jdbc.username>
  <jdbc.password>pass</jdbc.password>
 </properties>
 <build>
  <resources>
   <resource>
    <directory>src/main/resources</directory>
    <filtering>true</filtering>
   </resource>
  </resources>
 </build>
Właściwości te mogą być różne dla różnych środowisk. Warto wtedy stworzyć profile mavena, a w nich właściwości specyficzne dla konkretnego środowiska:
<profiles>
  <profile>
   <id>prod</id>
   <properties>
    <jdbc.url>jdbc:hsqldb:hsql://production:9002</jdbc.url>
   </properties>
  </profile>
  <profile>
   <id>test</id>
   <properties>
    <jdbc.url>jdbc:hsqldb:hsql://testowa:9002</jdbc.url>
   </properties>
  </profile>
 </profiles>
Dzięki temu podczas budowania projektu można wybrać odpowiedni profil i automatycznie zostaną wybrane odpowiednie właściwości:
mvn clean install -P prod
Jeśli serwerem aplikacyjnym jest Tomcat, to warto wykorzystać profile do konfiguracji wdrażania aplikacji przez wtyczkę maven'a . Można też połączyć rozwiązanie pierwsze i drugie poprzez zamieszczenie we właściwościach pliku pom.xml jedynie nazwy plików dla PropertyPlaceholderConfigurer, by w zależności od profilu uwzględniany odpowiedni plik z właściwościami.

Kategorie w testach JUnit

Dobrze zaprojektowany test jednostkowy powinien testować jedną klasę, z zaślepionymi zależnościami (np z EasyMock) i wykonywać się szybko, żeby nie było pokusy ich omijania. Uruchamiając je przy każdym buildzie aplikacji wychwytuje się szybko wiele błędów regresji, co oszczędza dużo czasu.

Jednak JUnit, choć przeznaczony jest głównie do testów jednostkowych, można użyć również do bardziej złożonych testów: integracyjnych, lub wydajnościowych, które wykonuje się rzadziej niż jednostkowe. Tylko trzeba by je jakoś oddzielić od siebie, np. pogrupować w kategorie i stworzyć zestawy (TestSuite) które będą wykonywać poszczególne grupy testów. Wtedy przydatna okazuje się adnotacja @Category, która określa kategorię testu. Kategorią może być dowolna klasa, lub interfejs. Przykładowo:
public interface SlowTest {}
public interface FastTest {}
stworzyłem dwa interfejsy, które użyję jako kategorie testów:
public class SimpleTest {
    @Category(FastTest.class)
    @Test
    public void someTest() {
        // ...
    }

    @Category(SlowTest.class)
    @Test
    public void someTest() {
        // ...
    }
}
oraz TestSuite, w którym oprócz klas testowych określam kategorię testów, jaką ma wykonać:
@RunWith(Categories.class)
@IncludeCategory(FastTests.class)
@SuiteClasses({ SimpleTest.class })
public class SimpleTestSuite {
}
Adnotacją @Category można oznaczać poszczególne metody lub całe klasy. Powyższy przykład wykonuje testy oznaczone kategorią FastTests.class. Odwrotne zachowanie nastąpi po użyciu adnotacji @ExcludeCategory(FastTest.class).

W eclipse można skonfigurować sobie czy mają być wykonywane wszystkie testy, czy na przykład tylko wybrany zestaw (Run Configurations -> JUnit). Myślę, że dosyć wygodnym rozwiązaniem jest stworzyć sobie dwie konfiguracje, jedną uruchamiającą zestaw (TestSuite) z testami o kategorii FastTests i drugą uruchamiającą zestaw z wszystkimi testami.

Project Lombok - skróć kod o 90%

Ten post jest o pomysłowym i bardzo prostym narzędziu, które pozwala uporać się z jedną z javovych niedogodności.

Załóżmy, że potrzebujesz klasy Punkt, z dwoma polami typu double. Aby było zgodnie z dobrymi praktykami programowania, pola będą miały dostęp prywatny, wygenerujesz gettery i settery, konstruktor, toString, equals i hashCode, przez co kod klasy urośnie do rozmiaru 965 znaków:
public class Punkt {
 private final double x;
 private final double y;

 public Punkt(double x, double y) {
  super();
  this.x = x;
  this.y = y;
 }

 @Override
 public String toString() {
  return "Punkt [x=" + x + ", y=" + y + "]";
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  long temp;
  temp = Double.doubleToLongBits(x);
  result = prime * result + (int) (temp ^ (temp >>> 32));
  temp = Double.doubleToLongBits(y);
  result = prime * result + (int) (temp ^ (temp >>> 32));
  return result;
 }

 @Override
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Punkt other = (Punkt) obj;
  if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
   return false;
  if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
   return false;
  return true;
 }

 public double getX() {
  return x;
 }

 public double getY() {
  return y;
 }
Co prawda kod ten generuje IDE, ale czy nie wolałbyś, żeby klasa Punkt wyglądała na przykład tak:
import lombok.Data;
public @Data class Punkt {
 private final double x;
 private final double y;
}
To 94 znaków zamiast 965. Jak dla mnie pomysł świetny. Na stronie http://projectlombok.org/ jest 3 minutowy filmik, po obejrzeniu którego nie będzie problemu z użyciem we własnym projekcie.

Wrażenia ze spotkania Łódź JUG

Tydzień temu miałem przyjemność uczestniczyć w spotkaniu JUG Łódź i chciałbym się podzielić wrażeniami.

Spotkanie odbyło się na nowoczesnej auli na DMCS'ie Politechniki Łódzkiej i w ramach niego Paweł Włodarski, programista z TomTom'a, przedstawił prezentację na temat projektu, w którego realizację jest zaangażowany, oraz wykorzystywanych przy tym projekcie technologii i narzędzi. Krótko omówił główny problem, polegający na tym, że pojedynczy komputer nie dysponuje wystarczającą mocą obliczeniową, aby obsłużyć duże ilości zapytań do map udostępnianych przez TomTom, co oznacza potrzebę rozproszenia obliczeń pomiędzy kilka maszyn. Opowiedział również nieco o Hadoop, GridGain, Spring Batch i ActiveMQ, które można użyć do rozwiązania tego problemu, oraz o tym dlaczego zdecydowali się na GridGain. Następnie uchylił nieco rąbka tajemnicy na temat tego jak organizowana jest praca zespołu programistów w TomTom'ie, czyli podzielił się doświadczeniami z pracą w Scrumie. Słuchaczy wyraźnie to zainteresowało, wywiązała się luźna dyskusja na temat prowadzenia projektów i pracy zespołu. W między czasie zostało wspomniane o podejściu do testowania i użyciu JUnit, DbUnit, EasyMock, o Sonarze za pomocą którego badana jest jakość kodu, o Liquibase służącym do wersjonowania bazy, o Spring AOP, Hibernate Validator, ale raczej pobieżnie. Czasami brakowało jakiegoś wizualnego przedstawienia tych narzędzi, w postaci rysunku, bądź diagramu.

Ogólnie prezentacja była ciekawa, można było dowiedzieć się jak wygląda realizacja projektu w innej firmie. Trochę zaskoczony byłem tym, że przyszło niewiele osób, raczej nie było głośno o tym spotkaniu. O kolejnych można dowiedzieć się z kalendarza spotkań.


ClassNotFoundException przy uruchamianiu projektu Spring+Maven+Tomcat w Eclipse

Podczas wdrażania na serwer Tomcat projektu stworzonego przy użyciu Spring'a i Maven'a w Eclipse można napotkać na problem objawiający się wyjątkiem:

SEVERE: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener
java.lang.ClassNotFoundException: org.springframework.web.context.ContextLoaderListener 

Problem taki może być powodowany brakiem listenera uruchamiającego kontekst Springa w pliku web.xml;
<context-param>
 <param-name>contextConfigLocation</param-name>
 <param-value>/WEB-INF/Spring/applicationContext.xml</param-value>
</context-param>
 
<listener>
    <listener-class>
          org.springframework.web.context.ContextLoaderListener
    </listener-class>
</listener>

Jeśli to nie jest przyczyną problemu, to należy sprawdzić zawartość katalogu: {workspace}\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\{project}\WEB-INF\lib
Dodając serwer w środowisku Eclipse, tworzy on katalog w przestrzeni roboczej (workspace) i powinny się tam znaleźć wdrażane aplikacje webowe. Podany wyżej katalog powinien zawierać biblioteki projektu, jeśli nie, to jest to przyczyną problemu.

Rozwiązania są następujące:
1. Biblioteki te można skopiować ręcznie
2. Można również dodać wpis w Deployment Assembly we właściwościach projektu:
Należy dodać zależności Maven'a, oraz projekty zależne (o ile takie są).
3. Można też zainstalować dodatki integrujące wtyczkę M2Eclipse z WTP (Web Tools Platform). W tym celu trzeba poprzez Help->Install new software i używając adresu http://m2eclipse.sonatype.org/sites/m2e-extras wybrać Maven Integration for Eclipse WTP:
Po kliknięciu kliknąć prawym przyciskiem myszy na projekcie i wybraniu Maven->Update project configuration wpisy Deployment Assembly powinny się dodać automatycznie:

Przesłanianie w Javie i jego kruczki

Na początek zagadka. Są klasy:
public class Bazowa {
 public int i = 5;
 public int getI1() {
  return i;
 }
 public int getI2() {
  return i;
 }
}
public class Pochodna extends Bazowa {
 public int i = 0;
 public int getI1() {
  return i;
 }
        public void sth(){};
}
Jaki będzie wynik uruchomienia kodu:
public class Test {
 public static void main(String[] args) {
  Bazowa b = new Pochodna();
  System.out.print(b.i);
  System.out.print(b.getI1());
  System.out.print(b.getI2());

  System.out.println("");
  Pochodna p = new Pochodna();
  System.out.print(p.i);
  System.out.print(p.getI1());
  System.out.print(p.getI2());
 }
}
Jeśli bez wahania odpowiedziałeś:
505
005
to brawo. Ten post nie będzie dla Ciebie niczym odkrywczym.

W powyższym kodzie zadeklarowano dwie zmienne lokalne (b,p). Jedna z nich jest referencją typu Bazowa, druga typu Pochodna. Dzięki polimorfizmowi referencja typu Bazowa może odnosić do obiektu typu pochodnego Pochodna. Mimo, że referencje są różnego typu, obiekty na który wskazują mają taki sam typ.
Korzystając z referencji b, możemy wykonywać widoczne metody klasy Bazowa. Dlatego nie można zrobić takiego wywołania: b.sth(), mimo, że właściwy obiekt taką metodę zawiera.

W klasie Pochodna została przesłonięta metoda getI1(), która jest zadeklarowaną w klasie Bazowa i wykonując b.getI1() w 6. linii, JVM "wie" jakiego typu jest obiekt i wykona odpowiednią wersję metody, czyli tę z klasy Pochodna. Dlatego rezultatem jest wartość 0.

Nie dotyczy to jednak zmiennych. Dlatego b.i zawiera wartość 5, czyli wartość zmiennej i w klasie Bazowa, która jest zupełnie inną zmienną niż zmienna i w klasie Pochodna, mimo, że posiadają taką samą nazwę.

Wywołanie p.getI2() zwróci 5, ponieważ klasa Pochodna dziedziczy z Bazowa metodę getI2(), zwracająca wartość zmiennej i klasy Bazowa.

Na koniec dodam kilka innych zasad dotyczących przesłaniania:
  1. metody, które nie są dziedziczone, nie mogą zostać przesłonięte
  2. przesłaniające metody mogą mieć mniej restrykcyjny poziom dostępu
  3. przesłaniające metody mogą rzucać taki sam, lub mniejszy zakres wyjątków (nie licząc wyjątków typu runtime)

ad.1. Przykład:

package jeden;
public class Bazowa {
 public int i = 5;
 int getI1() {
  return i;
 }
}
package dwa;
import jeden.Bazowa;
public class Pochodna extends Bazowa {
 public int i = 0;
 public int getI1() {
  return i;
 }
}
public class Test {
 public static void main(String[] args) {
  Bazowa b = new Pochodna();
  System.out.print(b.getI1());
 }
}
Tutaj, w przeciwieństwie do wcześniejszego przykładu, rezultatem będzie "5", ponieważ Pochodna nie dziedziczy metody getI1() (klasy są w różnych paczkach, a metoda ma domyślny poziom dostępu), dlatego metoda ta nie może być przesłonięta.

ad.2. Gdyby w poprzednim przykładzie zmienić poziom dostępu metody getI1() w klasie Bazowa na protected, wtedy nie możliwe byłoby przesłonięcie metody i rezultat wyniósłby "0""

MiKTeX i Windows API error 87

Ostatnio instalując nowe pakiety do MiKTeX'a natrafiłem na niewiele mówiący błąd:

Windows API error 87
Straciłem na to sporo czasu, ale rozwiązanie jest bardzo proste: należy zamknąć wcześniej procesy LaTeX-a, które blokują instalację pakietów, np. pdflatex.exe - po paru nieudanych próbach instalacji można zobaczyć taki rezultat w menedżerze zadań:

Mam nadzieję, że ta informacja zaoszczędzi komuś trochę czasu.