Development z Zapomnianej Strony

..:: Paweł Hofman .NET Portal ::..

Nie wiem czemu, ale wydawało mi się, że czasy, gdy firmy telekomunikacyjne traktują klienta jak szmatę odeszły w niepamięć. Oj, jak bardzo się pomyliłem i jak dobitnie wykazała się firma Netia S.A., aby mi to uświadomić.

Cała historia zaczęła się w połowie sierpnia, gdy zmieniłem biuro i zamówiłem na infolinii pakiet “Internet + telefon” w swojej nowej siedzibie. Wszystko pięknie, szybko, kurier przywiózł umowę do podpisania, za 2 tygodnie przyszedł monter i “coś” zrobił, a tydzień później przyjechał ruter i nagle zdziwko.

“Brak sygnału DSL”. Hmmm…

Miało działać, a coś jest nie tak i nie działa, ale to wiem i rozumiem tylko ja. Szybki telefon na infolinię – “wszystko aktywowane i w systemie widać, że śmiga”. To zróbmy kilka prostych testów. Dzwonię z telefonu stacjonarnego do siebie. Brawo, udało się, ale numer jakiś nie ten, co w umowie. Hmmm…. dziwne. To zadzwońmy na ten mój z umowy. O! ktoś odebrał i okazało się, że mieszka 3 ulice dalej, i twierdzi, że nagle telefon, z którego zrezygnował kilka miesięcy wcześniej “reaktywował się”!

Dzwonię ponownie na infolinię, i informuję ich, że aktywowali MÓJ Internet i MÓJ telefon u kogoś “przypadkowego”. “Już rejestruję zgłoszenie awarii. Sprawa zostanie natychmiast załatwiona”. Gdy w ciągu kolejnych 3-ch tygodni dzwoniłem tam jeszcze 4 razy i słyszałem dokładnie to samo, czasem przeplatane tekstem, że “zaraz się z Panem skontaktuje ekipa naprawcza”, to zacząłem nabierać podejrzeń, że ktoś mnie zbywa tanimi tekstami.

Czekam, nic innego nie pozostało. Czekałem 46 dni. W tym czasie, nikt z Netii S.A. się ze mną nie skontaktował, nikt nie napisał maila, a sprawa nadal została nierozwiązana. W ciągu tych dni Netia nie zrobiła dosłownie nic. Cóż… może dlatego, że była zajęta przejmowaniem kolejnej firmy, ale tego nie jestem pewien. Może wszyscy byli na urlopie. Może takie zachowanie to ich najnowsza dewiza. W ten też sposób z klienta, który sam przyszedł do nich, stałem się najbardziej rozgoryczonym złośliwcem.

Po wspomnianych 46-u dniach, zgłosiłem reklamację, że nie chcę płacić rachunków za kogoś, jeśli przypadkiem miałbym dostać jakąś fakturę, i w tym samym piśmie wypowiedziałem umowę. No proszę was, ile może czekać klient biznesowy!

Teraz uwaga, bo to co się stało później, przerosło moje najśmielsze oczekiwania. Firma Netia S.A. ze swoim flagowym oddziałem obsługi klienta, postanowiła kontynuować swoją w pełni nie-profesjonalną postawę, odpowiadając na moją reklamację – NEGATYWNIE! Ale uwaga, zrozumieli, że Internetu i telefonu nie mam, zatem anulują rozmowy prowadzone przez osoby trzecie. Jednakże wg nich umowę wypowiedziałem za wcześnie i obciążą mnie kosztami aktywacji. Pytanie tylko jakiej – bo ja w umowie mam konkretną lokalizację, a tam jak nic nie było, tak dalej nic nie ma! A za wcześnie, bo niby powinienem odczekać 70dni! Na dobicie, konsultant rozpatrujący reklamację stwierdził, że nie mam prawa odwołania ani odpowiedzi i jego decyzja jest ostateczna.

Jak we dwa dni później pozbierałem szczękę z ziemi, to tylko zostało mi stwierdzić kilka faktów i odesłać je ponownie na piśmie na skrytkę pocztową Netii w Katowicach. (Tak są pewne biura lokalne, które sprzedają te usługi, ale to już jest Internetia i nie bardzo chcą obsługiwać klientów, którzy podpisali umowy przez infolinie. Także przestroga – wszystkie reklamacje wysyłajcie zawsze na piśmie!).

A to, co stwierdziłem to:

  • w umowie mam konkretny lokal, gdzie aktywacja powinna mieć miejsce
  • umowa definiuje 40 dni od daty podpisania, jako ostateczny termin wykonania przez Netię S.A. aktywacji
  • brak możliwości odpowiedzi na odpowiedź łamie postanowienia regulaminu, na który wielokrotnie powoływał się konsultant
  • to umowa jest obowiązująca, a regulamin; regulamin reguluje to, co nie jest powiedziane w umowie
  • to po prostu najzwyklejsza próba wyłudzenia pieniędzy za niewykonaną usługę
  • podoba mi się to myślenie, że “jesteśmy dużą firmą – PŁAĆ, bo w sądzie z nami nie wygrasz”

Na koniec – cóż, minęło 65dni. Internetu dalej nie mam, do wyboru mam jeszcze TP S.A., bo nikt inny nie obsługuje centrum miasta. Dobrze, że to Wrocław, duże miasto, dobrze, że 21. wiek i premier Pawlak mówi o informatyzacji kraju. Ha! Oby tak dalej!



Dziś ukazała się długo oczekiwana nowa wersja moich bibliotek CodeTitans Libs v1.5. Zawiera ona szereg poprawek oraz nowych funkcjonalności.

Niewtajemniczonym, podpowiem, że służą one do: obsługi odczytu i zapisu w formacie JSON, wymiany danych z użyciem protokołu Bayeux oraz zawierają inne funkcjonalności potrzebne przy przenoszeniu programów z iPhone’a do Windows Phone 7.

Zapraszam do pobierania tutaj.



Kolejną ciekawostką, którą chciałbym omówić są zdalne repozytoria i dostęp do nich. O ile są to repozytoria tego samego typu (czyli np. Subversion połączone z Subversion – jako externals, czy Git pracujący z Gitem – jako submodules), to nie ma to większego znaczenia i obsługa jest bezproblemowa.

Ciekawsze są oczywiście krzyżówki. Jeśli przyjrzymy się bliżej pracy w Git, który musi korzystać z zewnętrznego repozytorium Subversion, to już wcale nie musi wyglądać prosto. Weźmy choćby mój projekt CodeTitans Libraries (wspomagający użycie protokołu Bayeux, standardu wymiany danych tekstowych JSON, InversionOfControl oraz udostępniający niektórych funkcjonalności znanych programistom iOS SDK w .NET-cie). Codeplex.com udostępnia go jako Subversion.

Przyjmijmy, że repozytorium Git, ma bardzo prostą strukturę złożoną z branchy master oraz develop. Pierwszy służy to przechowywania referencji stabilnych wersji kodu oraz ich etykietowania. Drugiego używamy do rozwijania i pracy nad aktualnym kodem.

Cel: gdzieś w katalogu Externals/CodeTitans chcielibyśmy mieć kod z też projektu.

W tym celu przede wszystkich będziemy potrzebowali dodatkowego brancha. Nazwijmy go codetitans_develop. Wybiegając do przodu - Git tak naprawdę skopiuje pełny kod źródłowy zewnętrznego projektu i sam będzie go wersjonował. Niestety minus jest taki, iż będzie on ten kod próbował umieścić w głównym folderze swojego własnego repozytorium, generując niezły chaos, przy złączeniu obu projektów (tego pierwotnie trzymanego w Gicie oraz nowego). Praca na dodatkowym branchu pozwoli nam zatem na wprowadzanie lokalnych zmian, np.:

  • przeniesienia plików do wspomnianego folderu Externals/CodeTitans
  • dokonywania innych drobnych modyfikacji (np. gdy chcemy bezpośrednio dodać pliki do solution, bez referencjonowania projektów biblioteki CodeTitans Libraries, to może widoczność zmienić z ‘public’ na ‘internal’)

A zatem, po kolei:

  1. Dodanie nowego brancha:

    git checkout –b codetitans_develop
  2. Dodanie referencji do zewnętrznego repozytorium SVN:

    git svn init https://codetitans.svn.codeplex.com/svn -R codetitans --prefix Externals/CodeTitans/ --ignore-paths="^[^/]+/(?:branches|tags|bin)"

    - lokalizacja – codeplex.com
    - nazwa lokalna – codetitans
    - ponieważ repozytorium to ma standardową strukturę, a nas interesuje jedynie ‘trunk’ – ignorujemy pozostałe foldery
  3. Pobieramy historię zmian z repozytorium SVN:

    git svn fetch codetitans

    (za pierwszym razem pobrane zostanie pełna historia, co może trochę trwać; dla oszczędzenia czasu plecam dodanie opcji “-r <rev>” z numerem ostatniej rewizji, która jest dla nas istotna; później już oczywiście tylko ostatnie zmiany będą synchronizowane)
  4. Poprzednie polecenie, de facto, pobiera pliki, ale nie są one jeszcze nigdzie widoczne. Aby stały się widoczne, musimy połączyć je (operacją merge) z aktualnym branchem (tym utworzonym i ustawionym jako aktualny w kroku 1.).
    Pytanie pozostaje, co mamy teraz łączyć.

    Lista wszystkich branchy (git branch –a) ujawnia, że zostało dodane wskazanie zewnętrzne na kod SVN. Wygląda ono mniej więcej tak:

    remotes/Externals/CodeTitans/git-svn

    I łączymy kod:

    git merge –squash remotes/Externals/CodeTitans/git-svn

    Uwaga! W przyszłości to właśnie tutaj, podczas operacji merge będzie występowało najwięcej konfliktów. Tutaj też ładnie je rozwiążemy, a osobny branch gwarantuje nam, że nie mieszamy kodu (i zmian w nim) projektu zewnętrznego z naszym produkcyjnym.
  5. Wprowadzamy wszystkie niezbędne poprawki (przenosimy, zmieniamy treść plików…). Oczywiście operacje te kończymy pojedynczym:

    git commit –a –m “<komunikat>
  6. Wracamy z powrotem na nasz główny branch:

    git checkout develop
  7. I dodajemy do niego wypielęgnowany kod zewnętrznego projektu, który idealnie pasuje do naszej struktury:

    git merge --no-ff codetitans_develop

    - --no-ff – bardzo ważna opcja dla późniejszej graficznej wizualizacji repozytorium i zmian na nim; otóż wszystkie zmiany z brancha codetitans_develop nie zostaną wessane do brancha aktywnego w tym momencie brancha develop

    Tutaj już nie powinny wystąpić żadne konflikty, bo kod jest jednak rozdzielny, a wszystkie zmiany odnośnie zewnętrznej biblioteki prowadzone były na branchu “codetitans_develop”.
  8. Aktualizacja:

    Kiedy nasz projekt Subversion ulegnie zmianie, musimy je mozolnie dodać na repozytorium Git. Niestety, aby wszystko pięknie działało, należy wykonać wszystkie kroki od 3-ego włącznie.

Salute!



Czasem zdarza się sytuacja, w której ktoś, kto ma dostęp tylko do odczytu do naszego repozytorium SVN, chciałby w nim coś jednak zapisać. Udało mu się (lub jej) rozwiązać jakąś usterkę czy problem, albo usprawnić działanie. Moglibyśmy w tym miejscu dodać uprawnienia do zapisu i byłoby po sprawie.

Jednak czy na pewno? Zawsze jakieś “ale”! Z czegoś wynikał przecież fakt, że ów użytkownik nie posiadał pełni praw.

  • Czy wspomniana zmiana ma w ogóle sens i czy w ogóle istnieje? (może ktoś tylko chce wyłudzić dostęp)
  • Czy zmiany są w pełni przetestowane?
  • Czy nie wprowadzają luk w zabezpieczeniach i innych ukrytych furtek?
  • Czy zgadzają się one z konwencją nazewniczą i stosują projektowe formatowanie kodu?
  • Czy będziemy pamiętać, o cofnięciu uprawnień, gdy zmiany zostaną już wysłane do repozytorium?

Wyjściem z tej sytuacji może okazać się utworzenie "łatki" (patcha), która zawierała będzie tylko proponowane zmiany w konkretnych plikach i lokalizacjach. Później przekazanie jej zaufanej osobie w projekcie, która zmiany te może przejrzeć, ocenić i ma prawa do zapisu na serwerze. Oto jak się za to zabrać:

  1. Zapamiętanie zmian:

    svn diff > fix.diff

    (Rozszerzenie diff jest rozpoznawane przez wiele edytorów, podświetlając składnię podczas przeglądania, niemniej jednak każde inne byłoby równie dobre)

  2. Aplikacja zmian:

    patch -p0 -i fix.diff

    Teraz można sprawdzić, co i gdzie się zmieniło i po przeglądzie (oraz koniecznie testach) wysłać do repozytorium na serwerze.
  3. Wysłanie na serwer:

    svn commit –m “<message> 


Jeżeli zależy nam na jakości wytwarzanego oprogramowania (co jest z góry wiadome, że tak) – to pojęcie Continous Integration nie powinno być obce. Mi osobiście do gustu przypadł projekt BuildBot. Jednym zdaniem – mały, prosty, obsługujący wiele platform, bardzo łatwo rozszerzalny i co więcej mamy pełny kod źródłowy, gdyby coś poszło nie tak…

Jak go zatem zainstalować na Windows XP (i późniejszym)? Twardzieli odsyłam do “oficjalnego” przewodnika. Ja postaram się dodać coś od siebie i zmniejszyć ilość potrzebnym kroków:

  1. Instalujemy Python 2.7.1. Inne (czyt.: wyższe) wersje Pythona mogą być nieco niewygodne, bo nie wszystkie ‘pakiety’ wymagane przez BuildBota mogą być już zaktualizowane, aby z nią działać i w chwili pisania, tak właśnie było.
  2. Do zmiennej systemowej PATH dodajemy odpowiednio katalogi (co skróci niektóre męki przy wykonywaniu poleceń z konsoli):
    • C:\Python27
    • C:\Python27\Scripts
      (najprościej chyba we Właściwościach Systemu –> Zaawansowane –> Zmienne środowiskowe i tam znajdziemy Path)
  3. Instalujemy twisted w wersji 10.2.
  4. Instalujemy SetupTools dla Pythona w wersji 2.7.1. Będą one potrzebne, aby później bez problemu pobrać z sieci wszystko to, czego jeszcze brakuje dla działającej instancji BuildBota.
  5. Teraz poleceniami instalujemy:
    • easy_install Zope.Inteface
    • easy_install jinja2
    • easy_install PyCrypto
    • easy_install PyOpenSSL
  6. Wreszcie pobieramy samego BuildBota (jako master i slave). Rozpakowujemy do katalogu C:\Temp.
  7. Instalujemy go:
    • python C:\Temp\buildbot-0.8.2\setup.py install
    • python C:\Temp\buildslave-0.8.2\setup.py install
  8. Sprawdzamy, czy wszystko działa:
    buildbot --version

    Powinno zwrócić numer wersji, a nie krzyczeć o brak zainstalowanego pakietu.
  9. Teraz pozostała już tylko własna konfiguracja maszyn budujących, dostępów do repozytoriów, nadobnych akcji, uruchamiania testów oraz przygotowywaniu release’a. O tym może opowiem coś niedługo. Dokumentacja natomiast opisuje wszystko bardzo czytelnie.

Oczywiście nie byłbym sobą, gdybym w tym momencie nie napotkał na problem oraz nie próbował ostrzec, że bardzo łatwo zmarnować wieczór i stracić dobry humor. Powód dość prozaiczny. Nie działa logowanie oraz podgląd wykonywanych poleceń, historia itp. Generalnie dużo rzeczy wygląda jak nieudane posuniecie i brak wsparcia dla Windowsa. Nic bardziej mylnego.  Winą obarczmy brak zgrania BuildBota z najnowszą wersją twisted. Bardzo ładnie opisano to w tickecie #1697. Z pomocą przychodzi nam tutaj oczywiście otwartość kodu tego projektu. W opisie problemu znajduje się odnośnik do poprawki. Aplikujemy ją bardzo prosto, nadpisując plik builder.py, tym, który ściągamy z repozytorium.

Uruchamiamy ponownie BuildBot mastera i gotowe!



Kontynuując post o samym tworzeniu repozytorium i dostępie do niego z poziomu terminala Linuxa, warto też wspomnieć, że tak samo łatwo skonfigurować go można na Windowsie.

Otóż tunel ssh definiujemy w sekcji [tunnels] pliku (ścieżka dla Windows 7):

C:\Users\<użytkownik>\AppData\Roaming\Subversion\config

 

Wygląda on mniej więcej tak:

ssh = “C:/Programy/Putty/plink.exe” –P <port SVN> –l <użytkownik>
                                  –i “C:/Users/<użytkownik>/.ssh/id_rsa”

Wyjaśniając:

  1. Przy użyciu programu plink (wchodzącego w skład ‘paczki’ putty, którą trzeba pobrać i zainstalować wcześniej), utrzymywane będzie połączenie ssh do serwera.
  2. Plik klucza prywatnego id_rsa, nie jest wymagany, a jedynie stanowi ułatwienie, aby nie wprowadzać hasła przy każdorazowym dostępie do serwera (a ich żądań może być wiele nawet przy jednorazowej aktualizacji plików, czy pobieraniu/przeglądaniu repozytorium).

 

 

Jest jeszcze druga wersja tego tunelu, która pozwala na jednoczesne korzystanie z TortoiseSVN. Wygląda ona tylko nieznacznie inaczej:

ssh = “C:/Program Files/TortoiseSVN/bin/TortoisePlink.exe” –P <port SVN> –l <użytkownik>
                                 –i “C:/Users/<użytkownik>/.ssh/id_rsa”

 

 

Do repozytorium dostajemy się później standardowo poleceniem:

svn co svn+ssh://<ścieżka>

oraz aktualizujemy:

svn up .

Gotowe!



Od jakiegoś już czasu firmware do kilku modeli firmy dLink (DI-524, DIR-628, DIR-655) posiadał dość znaczącą lukę w zabezpieczeniach obsługi protokołu HNAP. O tym jak przejąć kontrolę nad takim ruterem można poczytać tutaj.

Najnowsza aktualizacja do wersji v1.32EUb01 (z 02/10/2010) rozwiązuje ten problem. Niestety wprowadza dodatkowe niedogodności, o których nie ma ani słowa w ‘release notes’. Od tej chwili mimo, że w ustawieniach włączymy UPnP, opcja ta nie będzie dostępna. Zatem wszystkie programy, które używają tej funkcjonalności - automatycznie przestaną działać.

Niestety jedynym lekarstwem, jest nie aplikowanie tej aktualizacji i dalsze pozostawanie przy wersji v1.31EUb02 Beta (z 14/01/2010). A może ktoś zna lepszy sposób? Pozostaje mieć nadzieję, że firma dLink poprawi ten błąd wkrótce.



Kolejny dzień, kolejny problem z serii, “bo coś jest zepsute u podstaw” konfiguracji systemu… Zawczasu ostrzegam, że wpis ten jest wybitnie rozwlekły i nie jest przeznaczony dla osób niecierpliwych.

Zatem po kolei. Będąc zafascynowany “nowymi” rozwiązaniami, postanowiłem w ramach testów uruchomić przykłady z pakietu CometD i zapoznać się nieco z protokołem Bayeux (zainteresowanych tematem odsyłam do dokumentacji projektu). Oczywiście nic prostszego - wchodzimy na stronę projektu, pobieramy odpowiednie źródła, wypakowujemy CometD i uruchamiamy Jetty, które instaluje w sobie odpowiednie rozszerzenie. Wszystko bardzo ładnie w kilku zdaniach opisane jest tutaj.

Jakież wyrazy cisną nam się na usta, gdy ściągane są kolejne pakiety idące już prawie w gigabajty (do Javy, Apache, Maven2, itp…). Mija w końcu to niesłychanie długie 20 minut i oczom naszym ukazuje się Jetty w pełnej krasie. Ostrzegam, że jeszcze nawet nie zaczęliśmy grzebać w naszym testowym Debianie, a przecież chwilkę temu pobraliśmy najnowsze wersje całego oprogramowania na świecie.

~/cometd-2.0.0/cometd-demo$ mvn jetty:deploy-war
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'jetty'.
[INFO] ------------------------------------------------------------------------
[INFO] Building CometD :: Demo
[INFO] task-segment: [jetty:deploy-war]
[INFO] ------------------------------------------------------------------------
[INFO] Preparing jetty:deploy-war
[INFO] [enforcer:enforce {execution: enforce-plugin-versions}]
[INFO] [jetty:deploy-war]
[INFO] Configuring Jetty for project: CometD :: Demo
[INFO] Context path = /
[INFO] Tmp directory = /home/pawel/cometd-2.0.0/cometd-demo/target/tmp
[INFO] Web defaults = org/eclipse/jetty/webapp/webdefault.xml
[INFO] Web overrides = none
[INFO] Starting jetty 7.1.5.v20100705 ...
2010-09-05 01:29:34.971:INFO::jetty-7.1.5.v20100705
2010-09-05 01:29:37.803:WARN::EXCEPTION
java.lang.NullPointerException
    at javax.naming.spi.NamingManager.getPlusPath(libgcj.so.90)
    at javax.naming.spi.NamingManager.getStateToBind(libgcj.so.90)
    at org.eclipse.jetty.jndi.NamingContext.bind(NamingContext.java:337)
    at org.eclipse.jetty.jndi.NamingContext.bind(NamingContext.java:415)
    at org.eclipse.jetty.jndi.java.javaRootURLContext.(javaRootURLContext.java:78)
    at java.lang.Class.initializeClass(libgcj.so.90)
    at org.eclipse.jetty.jndi.java.javaURLContextFactory.getObjectInstance(javaURLContextFactory.java:63)
    at javax.naming.spi.NamingManager.getURLContext(libgcj.so.90)
    at javax.naming.spi.NamingManager.getURLContext(libgcj.so.90)
    at javax.naming.InitialContext.getURLOrDefaultInitCtx(libgcj.so.90)
    at javax.naming.InitialContext.lookup(libgcj.so.90)
    at org.eclipse.jetty.plus.webapp.EnvConfiguration.createEnvContext(EnvConfiguration.java:202)
    at org.eclipse.jetty.plus.webapp.EnvConfiguration.preConfigure(EnvConfiguration.java:62)
    at org.eclipse.jetty.webapp.WebAppContext.doStart(WebAppContext.java:378)
    at org.mortbay.jetty.plugin.JettyWebAppContext.doStart(JettyWebAppContext.java:114)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55)
    at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:165)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.doStart(ContextHandlerCollection.java:162)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55)
    at org.eclipse.jetty.server.handler.HandlerCollection.doStart(HandlerCollection.java:165)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55)
    at org.eclipse.jetty.server.handler.HandlerWrapper.doStart(HandlerWrapper.java:92)
    at org.eclipse.jetty.server.Server.doStart(Server.java:242)
    at org.mortbay.jetty.plugin.JettyServer.doStart(JettyServer.java:67)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:55)
    at org.mortbay.jetty.plugin.AbstractJettyMojo.startJetty(AbstractJettyMojo.java:437)
    at org.mortbay.jetty.plugin.AbstractJettyMojo.execute(AbstractJettyMojo.java:377)
    at org.mortbay.jetty.plugin.JettyRunWarMojo.execute(JettyRunWarMojo.java:68)
    at org.apache.maven.plugin.DefaultPluginManager.executeMojo(DefaultPluginManager.java:451)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoals(DefaultLifecycleExecutor.java:558)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeStandaloneGoal(DefaultLifecycleExecutor.java:512)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoal(DefaultLifecycleExecutor.java:482)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeGoalAndHandleFailures(DefaultLifecycleExecutor.java:330)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.executeTaskSegments(DefaultLifecycleExecutor.java:291)
    at org.apache.maven.lifecycle.DefaultLifecycleExecutor.execute(DefaultLifecycleExecutor.java:142)
    at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:336)
    at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:129)
    at org.apache.maven.cli.MavenCli.main(MavenCli.java:287)
    at java.lang.reflect.Method.invoke(libgcj.so.90)
    at org.codehaus.classworlds.Launcher.launchEnhanced(Launcher.java:315)
    at org.codehaus.classworlds.Launcher.launch(Launcher.java:255)
    at org.codehaus.classworlds.Launcher.mainWithExitCode(Launcher.java:430)
    at org.codehaus.classworlds.Launcher.main(Launcher.java:375)

A później jest już prościej, bo co sekundę wyskakuje wyjątek (co zdecydowanie lepiej rzuca się w oczy):

2010-09-05 01:38:42.200:WARN::EXCEPTION java.lang.ClassCastException: gnu.java.nio.ServerSocketChannelImpl cannot be cast to java.nio.channels.SocketChannel
    at org.eclipse.jetty.io.nio.SelectorManager$SelectSet.doSelect(SelectorManager.java:708)
    at org.eclipse.jetty.io.nio.SelectorManager.doSelect(SelectorManager.java:195)
    at org.eclipse.jetty.server.nio.SelectChannelConnector.accept(SelectChannelConnector.java:134)
    at org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:793)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:436)
    at java.lang.Thread.run(libgcj.so.90)

   

Jednym słowem czad!

No niby prosty przykład, a ile radości. Cóż, znowu ktoś zawinił. Tym bardziej ciekawe jest kto oraz że na forum projektu milczą o tym błędzie, jakby się wszyscy pod ziemię zapadli. Podpowiem, że projekty (CometD oraz sam Jetty) dostępne sąt od kilku ładnych lat i na pewno ktoś by się z tym problemem zetknął. Chyba nie jestem aż takim szczęściarzem. A może jednak to jest jakaś niszowa przypadłość? Bynajmniej. Troszkę szperania i co się okazuje. Otóż ani PATH ani JAVA_HOME nie pokazują lokalizacji JVM (Java Virtual Machine). Dokumentacja do Jetty mówi o tym wyraźnie. Ustawiamy ścieżkę do /usr/lib/jvm/java-1.5.0-gcj-xxx i wszystko powinno ruszyć. Teoretycznie. W praktyce, dostajemy dokładnie ten sam błąd. No jakoś się całe środowisko pokumało, że tylko jedna wersja Javy była zainstalowana i nawet bez JAVA_HOME udało się ją załadować. No to może jeszcze inaczej, może gdzieś, jakiś przełącznik, jakaś aktualizacja, patch rejestru (dobra to nie Windows), symlink? wrr, cokolwiek.

Otóż nie. To, co zazwyczaj okazuje się poprawne, zazwyczaj też siedzi na samym szarym końcu rozwiązań i ostatnie wpada do głowy. Cały JVM jest do wyrzucenia :) No bo przecież GNU Java nie jest zgodna z Sun Java (tu wstaw dowolną wersję), bo i po co? Jakichże to kolorów nabiera wtedy życie…

Tak, ale instalacja oryginalnej ‘sunowskiej’ Javy też nie jest wcale taka prosta. Aby uzyskać dostęp do wersji Javy (Sun JDK 1.5 i JDK 1.6), niezbędny jest na początku dodatkowy wpis w konfiguracji apt-get. Te JDK-i oznaczone są bowiem jako “non-free” i musimy zaakceptować dodatkową licencję zanim będziemy mogli z nich skorzystać.

  • Dopisujemy więc na końcu pliku konfiguracyjnego apt-get (/etc/apt/sources.list) następującą linijkę:

deb http://ftp.debian.org/debian lenny main contrib non-free

  • Odświeżamy listę dostępnych pakietów:

apt-get update

  • Instalujemy Javę (kawę i kolejne 20 min mamy już tym razem w pogotowiu):

apt-get install sun-java6-jdk

apt-get install sun-java5-jdk

  • Na koniec, ustawiamy ją jako domyślną w systemie:

update-java-alternatives -s java-6-sun
echo 'JAVA_HOME="/usr/lib/jvm/java-6-sun"' | tee -a /etc/environment

  • Gotowe! Uruchamiamy ponownie skrypt Maven, który uruchamia Jetty i instaluje w nim CometD. Tym razem z powodzeniem pod adresem http://localhost:8080/ mamy już działające przykłady i użycie Bayeux! Nic dodać nic ująć.

Jednak wieczór można uznać za udany. Szkoda tylko, że nie to mi go skradło, na co chciałem go przeznaczyć…

Dziękuję za uwagę.



Kiedy budzisz się pewnego dnia i od początku coś ci się nie układa. Nie wiesz do końca, o co chodzi, ale niektóre rzeczy dwoją i troją ci się w oczach. To jeszcze nie dowód na to, żeby odstawić mocniejsze trunki! To po prostu Visual Studio 2010 ześwirowało!

U mnie wyglądało to tak, że nie działał Backspace, Control i inne specjalne klawisze, zaś sam interfejs prezentował się tak, że każdy element menu i paska narzędziowego był zduplikowany kilka razy w pozornie losowych miejscach:

VisualStudio 2010 - podwójne menu 

VisualStudio2010 - podwójne paski narzędziowe

 

Wyglądało to na swój sposób tragicznie. Istnieje niestety tylko jedna opcja, która pozwala przywrócić IDE to stanu poprzedniego. Wszystkie innej (tj. próba zresetowania ustawień w Tools->Import/Export Settings…, czy usunięcie katalogu VisualStudio z Moich Dokumentów) spełzły na niczym.

A oto przepis, który wszystko przywraca do normalności:

devenv.exe /ResetUserData



Z radością donoszę, że konkurs organizowany razem z Wrocławską Grupą .NET doszedł do skutku. Rozpoczyna się on już dziś (choć konkretniej to wczoraj) - 15 czerwca 2010! Trwał będzie aż do 21 września 2010!

W konkursie do wygrania cenne nagrody - 3 imienne komercyjne licencje na użytkowanie najnowszej wersji PostSharp 2.0, których fundatorem jest Paweł Hofman (CodeTitans.pl). Dalsze szczegóły dostępne są na stronie organizatorów tutaj. Życzę sukcesów i dobrej zabawy!

 

 PostSharp Wroc.NET



Autor

Paweł Hofman [CodeTitans]

ASP.NET
C/C++, C#, Objective-C
SQL

License and Disclaimer

Zobacz mnie na GoldenLine

Zobacz mnie na LinkedIn
Supported by Polish SQL Server User Group (PLSSUG)

Supported by WrocNet.org

Zine.net.pl

Wpisy

Projekty

Moje projekty open-source:

Sign in