Archive for Kwiecień, 2010

Dodanie możliwości przeszukiwania plików PDF

Zdarza się, że w pliku PDF znajdują się zeskanowane strony, zapisane w formacie JPG. W dokumencie takim nie ma możliwości skorzystania z możliwości jego przeszukania lub indeksowania treści (tak to już bywa z grafiką). W znaczącym stopniu obniża to użyteczność takiego dokumentu. Istnieją jednak aplikacje, które potrafią rozpoznać tekst w takim dokumencie (wykorzystując OCR) oraz dodać przezroczystą warstwę w dokumencie, w której zostanie on umieszczony. Pozwoli to z jednej strony na przeszukanie dokumentu a z drugiej zostanie zachowana jego struktura. Jest to prostsze rozwiązanie niż rozpoznanie całej struktury dokumentu, łącznie z formatowaniem.

Jedną z takich aplikacji jest pdfocr. Automatycznie rozpoznaje tekst znajdujący się w pliku PDF i dodaje go do tego pliku.

Czym jest pdfocr

Aplikacja pdfocr jest skryptem napisanym w Ruby, który łączy kilka aplikacji w celu rozpoznania tekstu w dokumencie PDF.

W poszczególnych krokach aplikacja robi:

  • przy użyciu pdftk dzieli plik PDF na pojedyncze strony
  • przy użyciu pdfimages wyodrębnia dane graficzne
  • rozpoznaje tekst za pomocą aplikacji cuneiform
  • rozpoznany tekst jest dodawany do pliku PDF przy użyciu hocr2pdf
  • łączy wszystkie pliki za pomocą pdftk

Instalacja aplikacji pdfocr

W przypadku Ubuntu należy dodać odpowiednie repozytorium:

sudo add-apt-repository ppa:gezakovacs/pdfocr
sudo aptitude update
sudo aptitude install pdfocr

Po tych krokach aplikacja powinna zostać zainstalowana wraz z koniecznymi zależnościami.

Rozpoznanie teksty w pliku PDF

Rozpoznanie tekstu jest proste:

pdfocr -i plik_wejsciowy.pdf -o plik_wyjsciowy.pdf

Po zakończeniu działania, powinniśmy mieć plik PDF z załączonym rozpoznanym tekstem. Domyślnie używanym językiem użytym do rozpoznania tekstu jest język angielski. Można go zmienić używając przełącznika -l, wartości jakie może on przyjąć są zależne od języków obsługiwanych przez aplikację cuneiform:

$ cuneiform -l
Cuneiform for Linux 0.9.0
Supported languages: eng ger fra rus swe spa ita ruseng ukr srp hrv pol dan por dut cze rum hun bul slo lav lit est tur.

Niestety, przynajmniej w moim przypadku, jakość działania moduły OCR jest ledwie zadowalająca. W kilku dokumentach które sprawdzałem wiele słów nie zostało rozpoznanych poprawnie. Nawet pomimo tego, aplikacja może być przydatna, przynajmniej daje szansę na odszukanie jakieś informacji w tekście.

Źródła

Tags: , , , , , , , ,

Dodanie spisu treści do wpisu na blogu

Od czasu do czasu napiszę dłuższy wpis, który składa się z kilku różnych punktów i aż prosi się o umieszczeniu jakiegoś krótkiego spisu treści. Oczywiście, ręczne robienie go raczej w grę nie wchodzi. Na szczęście w przypadku WordPressa z pomocą może przyjść odpowiednia wtyczka: WordPress Easy Contents.

Instalacja wtyczki WordPress Easy Contents

Instalacja wtyczki jest prosta, wystarczy skorzystać z panelu administracyjnego. Najpierw należy ja odszukać:

Następnie wybrać opcję Instaluj. Wtyczka powinna zostać zainstalowana a po instalacji należy jeszcze ją aktywować.

Instalacja WordPress Easy Contents

Konfiguracja

W celu konfiguracji wtyczki należy w panelu administracyjny wybrać Ustawienia->Easy Contents. Zostanie wyświetlony formularz z kilkoma polami, które można zmodyfikować:

Warto zmienić pozycje:

  • Table heading – nagłówek jaki ma się wyświetlić w momencie generowania spisu treści
  • Elements to add to table – jakie elementy mają się pojawić w spisie treści (ja używam na stronie nagłówki począwszy od h2)
  • Shorten – po ilu znaków nagłówek ma zostać skrócony
  • Float – jeżeli ilość wpisów w spisie treści jest mniejsza od tej wartości to spis pojawi się po prawej stronie aktualnego wpisu, inaczej będzie osobnym akapitem

Użycie

Sposób użycia jest prosty, należy w miejscu gdzie ma się pojawić wygenerowany spis treści wstawić następujący ciąg znaków (działa przynajmniej w edytorze HTML):

<!--contents-->

Jak działa i wygląda przykładowy spis treści można zobaczyć na górze tej strony :).

Źródła

Tags: , , , , ,

Logowanie zadawanych zapytań do bazy danych przy użyciu log4jdbc

Duża (jeżeli nie większość) część aplikacji korzysta z bazy danych. Zapisuje oraz pobiera z niej informacje praktycznie na okrągło i co ważniejsze, nie jest w stanie poprawnie funkcjonować bez niej. W przypadku Javy połączenia realizowane z bazą danych są opisane poprzez standard JDBC (Java DataBase Connectivity). Praktycznie wszystkie sterowniki do baz danych relacyjnych opierają się na tym standardzie i realizują opisane w nim funkcje. Ponieważ aplikacje tak silnie zależą od baz danych, często pojawiają się różne problemy związane z ich użyciem. W celu ich analizy przydatne może być możliwość sprawdzenia, jak wyglądają poszczególne zapytania do bazy danych wysłane przez aplikację, łącznie z informacje jakie dane są w nich przesyłane.

Jedną z możliwości sprawdzenia (dokładnego) jak wyglądają poszczególne zapytania do bazy danych, jest użycie specjalnego sterownika pośredniczącego pomiędzy naszą aplikacją a serownikiem bazodanowym. Pośrednik ten umożliwia zalogowanie odpowiedniego zapytania zanim zostanie ono przekazane do sterownika odpowiedzialnego za połączenie się z bazą danych i wykonywania na niej operacji. Takim sterownikiem pośredniczącym jest log4jdbc.

Instalacja biblioteki w aplikacji

Pierwszym krokiem powinno być pobranie odpowiedniej wersji biblioteki ze strony log4jdbc. Należy zwrócić uwagę, że inne wersje są zalecane w zależności od tego, jaka wersja Javy zostanie użyta do uruchomienia aplikacji. W moim przypadku najwłaściwszy był plik log4jdbc4-1.2beta1.jar.

Powyższy plik JAR należy dołączyć do ścieżki aplikacji. Ja w swoim przypadku korzystałem z aplikacji uruchomionej na serwerze aplikacyjnym JBoss, więc skopiowałem ten plik do katalogu [cc]common/lib[/cci]. Pozostał jeszcze tylko restart serwera i biblioteka jest gotowa do użycia.

Inicjalizacja połączenia

Istnieją dwa sposoby inicjalizacja biblioteki: w kodzie aplikacji poprzez wywołanie odpowiedniej klasy oraz poprzez odpowiednie utworzenie łańcucha połączenia z bazą danych w standardzie JDBC.

Inicjalizacja log4jdbc w kodzie aplikacji

Oto przykładowy kod źródłowy, jaki należy użyć w aplikacji :

1
2
3
4
5
6
7
// get connection from datasource
Connection conn = dataSource.getConnection();

// wrap the connection with log4jdbc
conn = new net.sf.log4jdbc.ConnectionSpy(conn);

// now use Connection as normal (but it will be audited by log4jdbc)

Czyli najpierw tworzymy lub pozyskujemy połączenie do bazy danych, a następnie przekazujemy je do instancji klasy ConnectionSpy, która od tej pory będzie pośredniczyć w wywoływaniu odpowiedniego kodu i logowaniu informacji przez bibliotekę log4jdbc.

Inicjalizacja połączenia przy użyciu JDBC

Drugim sposobem jest zdefiniowanie połączenia do bazy danych przy użyciu łańcucha JDBC. Robi się to dodając informację o użyciu biblioteki log4jdbc w łańcuchu definiującym połączenie. Jeżeli podstawowe połączenie z bazą danych wygląda następująco (połączenie z bazą danych MySQL):

jdbc:mysql://localhost:3306/baza_danych

To należy dodać do łańcucha ten ciąg znaków log4jdbc, tak aby wyglądał on następująco:

jdbc:log4jdbc:mysql://localhost:3306/baza_danych

Jako klasę sterownika należy użyć klasy: net.sf.log4jdbc.DriverSpy. Przykładowa konfiguracja źródła danych dla JBossa wygląda następująco:

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8"?>
<datasources>
  <local-tx-datasource>
    <jndi-name>bazaDanychDS</jndi-name>
    <connection-url>jdbc:log4jdbc:mysql://localhost:3306/baza_danych</connection-url>
    <driver-class>net.sf.log4jdbc.DriverSpy</driver-class>
    <!-- definicja użytkownika, hasła, innych parametrów potrzebnych do połączenia z bazą danych -->
  </local-tx-datasource>
</datasources>

Biblioteka log4jdbc automatycznie postara się zaincjalizować odpowiednią klasę sterownika dla używanej bazy danych. Pełna list domyślnie używanych klas (w zależności od wybranej bazy danych) znajduje się na stronie domowej tego projektu, poniżej znajduje się wybór kilku najczęściej używanych:

Baza danych Klasa sterownika
Oracle oracle.jdbc.driver.OracleDriver
MySQL com.mysql.jdbc.Driver
PostgresSQL org.postgresql.Driver
HSQLDB org.hsqldb.jdbcDriver

Jeżeli istnieje potrzeba użycia innego sterownika do bazy danych, można te informacje przekazać do biblioteki konfigurując odpowiednią zmienną systemową log4jdbc.drivers:

-Dlog4jdbc.drivers=<driverclass>[,</driverclass><driverclass>...]

Teraz pozostaje uruchomić aplikację i w logach powinny znaleźć się informacje o zadawanych zapytaniach, o wartościach poszczególnych parametrów w zapytaniu (szczególnie pomocne w podczas używania klasy PreparedStatement).

Konfiguracja loggerów

Biblioteka log4jdbc definiuje pięć różnych loggerów odpowiedzialnych za zapisywanie różnych informacji dotyczących komunikacji z bazą danych. Każdy z nich może mieć ustawiony jeden z poziomów logowania: DEBUG, INFO, ERROR, FATAL.

Jeżeli wszystkie będą ustawione na poziom FATAL, to faktycznie log4jdbc zostanie wyłączony. Wtedy też nowo tworzone połączenie nie będą otaczane klasą ConnectionSpy tylko będzie to bezpośrednie połączenie z bazą danych. Taki sposób działania jest przydatny, ponieważ można nawet w środowisku produkcyjnym mieć zdefiniowane połączenie z bazą danych przy użyciu log4jdbc, ale włączać je za pomocą definicji odpowiednich zmiennych, bez potrzeby modyfikowania łańcucha definiującego połączenie.

Znaczenie pozostałych poziomów logowania:

  • ERROR – wypisanie stack trace w logu w przypadku wystąpienia wyjątku SQLException
  • INFO – dodanie wyświetlania poleceń SQL
  • DEBUG – dodanie nazwy klasy oraz numeru linii w której zostało wykonane dane polecenie SQL (powoduje także większy narzut czasowy na zapisanie takie informacji w logu)

Dostępne loggery:

Logger Opis
jdbc.sqlonly logowanie samych poleceń SQL, w przypadku korzystania z klasy PreparedStatement także zostaną podane wartości poszczególnych parametrów
jdbc.sqltiming informacje o czasie wykonywania poszczególnych zapytań SQL
jdbc.audit logowanie wszystkich wywołań JDBC poza wynikami (używane do wyszukiwania problemów z JDBC)
jdbc.resultset logowanie wszystkich wywołań do klasy ResultSet
jdbc.connection logowanie informacji o otwieraniu i zamykaniu połączeń, pomocne w przypadku wystąpienia problemów z wyciekami połączeń

Przykładowa fragment linii poleceń wywołującej JVM w którym są te loggery włączane:

-Djdbc.sqlonly=DEBUG -Djdbc.sqltiming=DEBUG -Djdbc.audit=INFO -Djdbc.connection=INFO

Źródła

Tags: , , , , , , , ,

Konfiguracja serwera NFS 3 w Debianie

NFS jest często używanym sieciowym systemem plików. Jego implementacja jest domyślnie dostępna praktycznie we wszystkich dystrybucjach Linuksa, więc bardzo łatwo go zintegrować z systemem plików po stronie gościa.

W przypadku Debiana instalacja jest dosyć prosta, sprowadza się do zainstalowania odpowiedniego pakietu oraz zdefiniowania eksportowanych udziałów.

Serwer NFS jest umieszczony w pakiecie o nazwie nfs-kernel-server, więc pierwszym krokiem jest jego instalacja:

# aptitude install nfs-kernel-server

Konfiguracja udostępnianych udziałów jest przechowywana w pliku /etc/exports, więc należy tam umieścić odpowiednie wpisy:

/exports 192.168.0.0/24(rw,no_subtree_check)

Powyższy wpis powoduje, że udział /exports będzie dostępny dla wszystkich łączących się z sieci 192.168.0.0/24 w trybie odczytu i zapisu.

Aby uruchomić serwer NFS należy teraz wywołać skrypt startowy:

# /etc/init.d/nfs-kernel-server start

Podczas startu serwera na Debianie Lenny wystąpił mi następujący błąd (związany z używaniem nowszego jądra niż standardowo instalowane z dystrybucją):

Not starting NFS kernel daemon: no support in current kernel.

Aby rozwiązać problem, należy zmodyfikować skrypt startowy i zakomentować linię [cii]69[/cci]:

66
67
68
69
70
# See if our running kernel supports the NFS kernel server
if [ -f /proc/kallsyms ] && ! grep -qE 'init_nf(sd|     )' /proc/kallsyms; then
        log_warning_msg "Not starting $DESC: no support in current kernel."
#       exit 0
fi

Opis błędu można znaleźć na stronach Debiana: nfs-kernel-server: init script incompatible with kernel 2.6.32

Tags: , , ,

Automatyczne uruchamianie serwera JBoss podczas startu systemu operacyjnego

Jeżeli używamy serwer JBoss na serwerze produkcyjnym, powinien on zostać skonfigurowany tak, aby uruchamiał się podczas startu aplikacji i zatrzymywał podczas jego wyłączanie.

Opis powstał w oparciu o Ubuntu, ale raczej nie powinno być większych problemów z przystosowaniem go do działania na innych systemach linuksowych. Prawdopodobnie wystarczy odpowiednio zmodyfikować ścieżki dostępu oraz skonfigurować odpowiednie linki uruchamiające JBossa w odpowiednim poziomie startu.

Skrypt startowy

JBoss przychodzi ze skryptami pozwalającymi na jego automatyczne uruchamianie. Można je znaleźć w katalogu $JBOSS_HOME/bin:

  • jboss_init_hpux.sh
  • jboss_init_redhat.sh
  • jboss_init_solaris.sh

Jak widać istnieją trzy skrypty przygotowanie do użycia w rożnych systemach operacyjnych. Nam potrzebny będzie jboss_init_redhat.sh

Pierwszym krokiem będzie skopiowanie pliku jboss_init_redhat.sh do katalogu /etc/init.d pod nową nazwą jboss:

# cp $JBOSS_HOME/bin/jboss_init_redhat.sh /etc/init.d/jboss

Teraz należy skrypt startowy odpowiednio zmodyfikować i dostosować do konfiguracji naszego systemu operacyjnego. Wszelkie modyfikacje powinny być wprowadzane w pliku /etc/init.d/jboss.

Utworzenie nieuprzywilejowanego użytkownika

Serwer aplikacji JBoss w swojej standardowej konfiguracji nie wymaga ani dostępu do uprzywilejowanych miejsc w systemie operacyjnym ani nie używa żadnych uprzywilejowanych portów. Z tego powodu powinien być on uruchamiany jako na uprawnieniach nieuprzywilejowanego użytkownika. Należy więc utworzyć odpowiedniego użytkownika i użyć go do uruchomienia serwera aplikacji:

# useradd -M jboss

Polecenie to spowoduje utworzenie użytkownika jboss wraz z grupą jboss ale bez tworzenia katalogu domowego, oraz bez definiowania hasła czy innych parametrów użytkownika.

Należy jeszcze odpowiednio zmienić uprawnienia katalogu z serwerem JBoss:

# chown -R jboss.jboss $JBOSS_HOME

Dostosowanie skryptu startowego

Teraz trzeba odpowiednio dostosować skrypt startowy do naszego systemu operacyjnego. Poniżej omówię konfigurację potrzebnych zmiennych środowiskowych, należy pamiętać aby odpowiednie modyfikacje wprowadzić w pliku /etc/init.d/jboss.

  • katalog domowy
    Konfiguracja katalogu domowego znajduje się w zmiennej $JBOSS_HOME. Należy ją zmodyfikować tak, aby wskazywała na katalog, gdzie znajduje się serwer aplikacji JBoss:

    18
    JBOSS_HOME="/opt/jboss-5.1.0.GA"
  • użytkownik nieuprzywilejowany
    Nazwa użytkownika na prawach którego ma zostać uruchomiony serwer aplikacji znajduje się w zmiennej $JBOSS_USER:

    21
    JBOSS_USER="jboss"

    Jeżeli nazwa użytkownika zostanie ustawiona na wartość RUNASIS to wtedy serwer JBoss zostanie uruchomiony bez zmiany użytkownika, czyli na uprawnienia tego użytkownika, który wywoła skrypt uruchomieniowy.

  • ścieżka do Javy
    Ścieżka dostępu do maszyny wirtualnej Javy znajduje się w zmiennej $JAVAPTH. Należy w niej podać katalog, w którym znajduje się aplikacja java.

    24
    JAVAPTH="/usr/lib/jvm/java-6-openjdk/bin"
  • definicja konfiguracji
    Kolejnym elementem jest podanie konfiguracji, która ma zostać uruchomiona.

    27
    JBOSS_CONF="default"
  • konfiguracja interfejsu sieciowego
    W domyślnej konfiguracji JBoss nasłuchuje na przychodzące połączenia tylko na lokalnym interfejsie sieciowym. Aby to zmienić, należy go odpowiednio o tym poinformaować. W skrypcie startowym służy do tego zmienna $JBOSS_HOST. Nie jest ona zdefiniowana w skrypcie, należy utworzyć ją samodzielnie. Należy dodać następującą linię (powinna być umieszczona przed linią 30, która zawiera definicję zmiennej $JBOSS_BIND_ADDR, w której $JBOSS_HOST jest wykorzystywana):

    28
    JBOSS_HOST="192.168.1.10"

Teraz pozostaje tylko sprawdzić, czy wszystkie wartości zostały zdefiniowane poprawnie i spróbować uruchomić serwera aplikacji:

# /etc/init.d/jboss start

Automatyczne uruchamianie serwera JBoss

Ostatnim krokiem jest zdefiniowanie w jakich poziomach startu ma być automatycznie uruchamiany serwera aplikacji, czy należy utworzyć odpowiednie linki z pliku /etc/init.d/jboss. W systemach opartych o Debiana można w tym celu użyć komendy update-rc.d:

# update-rc.d jboss defaults

Spowoduje to utworzenie odpowiednich linków pozwalających na uruchomienie serwera w trakcie uruchamianie komputera oraz zatrzymanie go w trakcie wyłączania systemu operacyjnego.

W przypadku systemów z rodziny RedHata należy dodać jeszcze dwie linijki do pliku /etc/init.d/jboss:

7
8
# chkconfig: - 90 10
# description: Run JBoss Server

Pozwalają one na możliwość utworzenia odpowiednich linków przez polecenie chkconfig:

# chkconfig jboss on

Problem z wyłączeniem serwera

Może wystąpić błąd podczas próby wyłączenia serwera przy użyciu komendy:

# /etc/init.d/jboss stop

Błąd jest następujący:

Exception in thread "main" javax.naming.CommunicationException: Could not obtain connection to any of these urls: localhost:1099 [Root exception is javax.naming.CommunicationException: Failed to connect to server localhost/127.0.0.1:1099 [Root exception is javax.naming.ServiceUnavailableException: Failed to connect to server localhost/127.0.0.1:1099 [Root exception is java.net.ConnectException: Connection refused]]]
        at org.jnp.interfaces.NamingContext.checkRef(NamingContext.java:1763)
        at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:693)
        at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:686)
        at javax.naming.InitialContext.lookup(InitialContext.java:409)
        at org.jboss.Shutdown.main(Shutdown.java:219)
Caused by: javax.naming.CommunicationException: Failed to connect to server localhost/127.0.0.1:1099 [Root exception is javax.naming.ServiceUnavailableException: Failed to connect to server localhost/127.0.0.1:1099 [Root exception is java.net.ConnectException: Connection refused]]
        at org.jnp.interfaces.NamingContext.getServer(NamingContext.java:335)
        at org.jnp.interfaces.NamingContext.checkRef(NamingContext.java:1734)
        ... 4 more
Caused by: javax.naming.ServiceUnavailableException: Failed to connect to server localhost/127.0.0.1:1099 [Root exception is java.net.ConnectException: Connection refused]
        at org.jnp.interfaces.NamingContext.getServer(NamingContext.java:305)
        ... 5 more
Caused by: java.net.ConnectException: Connection refused
        at java.net.PlainSocketImpl.socketConnect(Native Method)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:310)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:176)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:163)
        at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:384)
        at java.net.Socket.connect(Socket.java:546)
        at org.jnp.interfaces.TimedSocketFactory.createSocket(TimedSocketFactory.java:97)
        at org.jnp.interfaces.TimedSocketFactory.createSocket(TimedSocketFactory.java:82)
        at org.jnp.interfaces.NamingContext.getServer(NamingContext.java:301)
        ... 5 more

Pojawia się on wtedy, gdy serwer JBoss będzie nasłuchiwał na innym interfejsie sieciowym niż localhost. Spowodowany on jest błędem w skrypcie startującym, który podczas próby wywoływania w serwerze akcji jego zamknięcie nie bierze pod uwagę zawartości zmiennej $JBOSS_HOST. Rozwiązanie jest proste, wystarczy wskazać odpowiedniej komendzie adres IP na którym nasłuchuje serwer.

Definicja komendy wywołującej zamknięcie serwera wygląda następująco:

62
JBOSS_CMD_STOP=${JBOSS_CMD_STOP:-"java -classpath $JBOSSCP org.jboss.Shutdown --shutdown"}

Wystarczy teraz dodać do niej następujący ciąg -s jnp://${JBOSS_HOST}:1099, tak aby ta linia miała następującą postać:

62
JBOSS_CMD_STOP=${JBOSS_CMD_STOP:-"java -classpath $JBOSSCP org.jboss.Shutdown --shutdown -s jnp://${JBOSS_HOST}:1099"}

Teraz wywołania akcji stop powinno już działać bez problemów.

Nie należy tej zmiany wprowadzać, gdy nie została zdefiniowana zmienna $JBOSS_HOST.

Należy także pamiętać, że jeżeli dostęp do konsoli JMX został zezwolony tylko autoryzowanym użytkownikom, to należy jeszcze do powyższego skryptu dodać parametry określające nazwę użytkownika oraz jego hasło. W takie sytuacji powyższa linia powinna wyglądać następująco:

62
JBOSS_CMD_STOP=${JBOSS_CMD_STOP:-"java -classpath $JBOSSCP org.jboss.Shutdown --shutdown -s jnp://${JBOSS_HOST}:1099 -u UZYTKOWNIK -p HASLO"}

Źródła

Tags: , , , ,