<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1707592354142736969</id><updated>2011-07-28T21:15:50.819-07:00</updated><category term='ApplicationDomain'/><category term='GWT'/><category term='Exception'/><category term='flex 3'/><category term='Javascript'/><category term='IceFaces'/><category term='Projekt'/><category term='Monitor'/><category term='JSF'/><category term='LCD'/><category term='modules'/><category term='SSB'/><category term='RichFaces'/><category term='Problem'/><category term='59Hz'/><category term='Error #1065'/><category term='60Hz'/><category term='Wydajność'/><category term='Zarządzanie'/><title type='text'>Exverxes Tech Blog</title><subtitle type='html'>Blog poświęcony w większości programowaniu. Czasami mogą się tutaj znaleźć również inne informacje, którymi chciał bym się podzielić.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>13</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-6361843638254670825</id><published>2010-09-30T00:39:00.000-07:00</published><updated>2010-09-30T01:41:36.337-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='modules'/><category scheme='http://www.blogger.com/atom/ns#' term='flex 3'/><category scheme='http://www.blogger.com/atom/ns#' term='ApplicationDomain'/><category scheme='http://www.blogger.com/atom/ns#' term='Error #1065'/><title type='text'>Jak w zasadzie działają moduły flexa</title><content type='html'>Ponieważ straciłem ostatnio sporo czasu na próbach podłączenia modułu do istniejącej już aplikacji (nie mając dostępu do aplikacji/źródeł), postanowiłem podzielić się swoimi przemyśleniami.&lt;br /&gt;&lt;br /&gt;Generalnie, flex jako flex jest bardzo fajnym narzędziem, ale jak każda technologia ma trochę problemów. Ja ostatnio natrafiłem na problemy związane z podłączeniem modułu który komunikował się za pomocą WebService.  Warto nadmienić, że sam moduł, został przygotowany nie jako moduł, ale jako osobna aplikacja webowa, co dostarczyło mi dodatkowych problemów. Przejdźmy do sedna:&lt;br /&gt;&lt;br /&gt;Moduły/Aplkiacje flexowe można ładować na kilka sposobów. Po pierwsze możemy skorzystać z loadera i przy jego pomocy załadować aplikację. Przykładowy kod wygląda następująco:&lt;br /&gt;MXML:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&lt;/code&gt;&lt;pre&gt; &amp;lt;mx:moduleloader url="SomeModule.swf"/&amp;gt;&lt;/pre&gt;&lt;br /&gt;AS:&lt;br /&gt;&lt;code&gt;&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;var loader : ModuleLoader = new ModuleLoader();&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;loader.url = "SomeModule.swf";&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;contentPanel.addChild(loader);&lt;span style="font-family:monospace;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Sprawy oczywiście się dalej komplikują, bo jak można się domyślić, nie jest to jedyny sposób ładowania modułów. Po pierwsze możemy użyć różnych loaderów. Dla przykładu &lt;i&gt;SwfModuleLoader&lt;/i&gt; nadaje się do załadowania aplikacji a nie modułu (jak również swf wykonanego we flashu). Zastosowanie tego loadera potrafi rozwiązać wiele problemów które można spotkać z innymi loaderami, w szczególności można przy jego pomocy prawidłowo ustawić moduł na ekranie. Obsługuje on również eventy związane ze zmianą wielkości obszaru aplikacji. Użycie tego loadera rozwiązało moje problemy związane z ładowaniem modułu (o których napiszę dalej).&lt;br /&gt;&lt;br /&gt;Aby sprawy dalej skomplikować, można również użyć &lt;i&gt;ModuleManager&lt;/i&gt;, co zostało zastosowane w przypadku aplikacji do której się podłączałem. Stosowanie ModuleManager wymaga jednak nieco innego podejścia. Jest to element który zarządza załadowanymi modułami. Przykładowy kod zastosowania wygląda następująco:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;var moduleInfo : IModuleInfo;&lt;br /&gt;moduleInfo = ModuleManager.getModule(_url);&lt;br /&gt;moduleInfo.load();&lt;br /&gt;var module:Object = moduleInfo.factory.create();&lt;br /&gt;container.addChild(module);&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Jak widać, flex pozwala na na prawdę dużą dowolność. W szczególności można użyć po prostu &lt;i&gt;Loader&lt;/i&gt;. W zasadzie nie było by to aż tak skomplikowane, gdyby nie fakt, że dodatkowo jest jeszcze takie pojęcie jak &lt;i&gt;ApplicationDomain&lt;/i&gt;.  Domena aplikacji de-facto oznacza, w jakim środowisku dany moduł powinien zostać załadowany.  Domenę ładuje się następująco:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;var &lt;i&gt;applicationDomain&lt;/i&gt;:ApplicationDomain = ...;&lt;br /&gt;&lt;br /&gt;-----------------------------------------------------&lt;br /&gt;&lt;br /&gt;var moduleInfo : IModuleInfo;&lt;br /&gt;moduleInfo = ModuleManager.getModule(_url);&lt;br /&gt;moduleInfo.load(&lt;i&gt;applicationDomain&lt;/i&gt;);&lt;br /&gt;&lt;br /&gt;&lt;code&gt;-----------------------------------------------&lt;br /&gt;&lt;br /&gt;var loader : ModuleLoader = new ModuleLoader();&lt;span&gt;&lt;br /&gt;&lt;/span&gt;loader.url = "SomeModule.swf";&lt;span&gt;&lt;br /&gt;&lt;/span&gt;contentPanel.addChild(loader, &lt;i&gt;applicationDomain&lt;/i&gt;);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Pod zmienną applicationDomain z przykładu można podstawić 3 (w zasadzie 4) wartości. Pierwsza (domyślna) to wartość:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;var applicationDomain:ApplicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain);&lt;br /&gt;&lt;br /&gt;lub&lt;br /&gt;&lt;br /&gt;var applicationDomain:ApplicationDomain = null;&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Tego typu zapis oznacza, że moduł jest modułem dzieckiem. Do rodzica można dostać się poprzez &lt;i&gt;ApplicationDomain.parrentDomain&lt;/i&gt;. Niestety, jest to tylko teoria. Obie domeny co prawda są odseparowane, ale jeżeli korzystamy w naszej aplikacji z singletonów, to będą się one zachowywać na dwa sposoby. Albo będą modułowe, albo aplikacyjne. Jeżeli singleton nie został zainicjalizowany w aplikacji (a wystarczy tylko wspomnienie takie jak &lt;i&gt;var sig:SomeSingleton = null&lt;/i&gt;), to jego zasięg będzie dotyczył tylko modułu (nawet, jeżeli występuje w innych modułach). Natomiast, jeżeli został zainicjalizowany w aplikacji, to będzie to singleton aplikacyjny. Przypomnę, że w moim wypadku nie miałem dostępu do kodu aplikacji, a więc nie byłem w stanie sprawdzić, czy singleton którego używam, był użyty w aplikacji.&lt;br /&gt;Samo to nie jest jeszcze straszne, ale są dalsze konsekwencje. Może wystąpić sytuacja, w której sigleton dynamicznie powołuje do instnienia obiekty klas (po nazwie klasy). Takie zachowanie ma na przykład &lt;i&gt;SchemaTypeRegistry.getInstance().registerClass(...)&lt;/i&gt; używane do rejestrowania klas dla &lt;i&gt;WebService&lt;/i&gt;.  Nagle pojawia się &lt;i&gt;Error #1065&lt;/i&gt;, oznaczający, że nie można załadować klasy, ponieważ nie została ona dołączona do skompilowanego kodu. Dokładnie rzecz ujmując ApplicationDomain znajdujący się w aplikacji (w przypadku singletona aplikacyjnego) próbuje powołać do życia obiekt, dla którego klasa znana jest tylko w ApplicationDomain modułu. Osobiści podejrzewam, że jest to jakiś błąd w flexie, ale jak szukałem informacji na temat, to przekrój czasowy w jakim można znaleźć posty na ten temat jest imponujący (2005-2010). Nawet Flex 4 posiada bardzo podobnie wyglądający błąd.&lt;br /&gt;&lt;br /&gt;Jak pisałem, są jeszcze 2 warianty przypisania AppliacationDomain:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;var applicationDomain:ApplicationDomain = new ApplicationDomain();&lt;br /&gt;&lt;br /&gt;oraz&lt;br /&gt;&lt;br /&gt;var applicationDomain:ApplicationDomain = ApplicationDomain.curentDomain;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Pierwszy sposób tworzy całkowicie odrębną domenę aplikacji. Osobiście tematu nie zbadałem, ale to co mogę powiedzieć, to to, że aplikacja nie chciała mi się uruchomić z modułem, musiałem zmienić moduł na aplikację, żeby osiągnąć jakikolwiek efekt.&lt;br /&gt;&lt;br /&gt;Ostatni sposób, tworzy wspólną domenę dla modułu oraz aplikacji. Takie podejście powoduje, że nagle klasy których mi brakowało, znajdują się w tej samej domenie co aplikacja. Oznacza to, że mój problem przestaje istnieć, jednak warto wspomnieć, że nie ma również separacji modułu i aplikacji.&lt;br /&gt;&lt;br /&gt;Więcej informacji można znaleźć w tych artykułach:&lt;br /&gt;http://livedocs.adobe.com/flex/3/loading_applications.pdf&lt;br /&gt;http://livedocs.adobe.com/flex/3/html/help.html?content=modular_5.html&lt;br /&gt;http://life.neophi.com/danielr/2006/07/flex_2_runtime_error_1065.html&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-6361843638254670825?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/6361843638254670825/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2010/09/jak-w-zasadzie-dziaaja-moduy-flexa.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/6361843638254670825'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/6361843638254670825'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2010/09/jak-w-zasadzie-dziaaja-moduy-flexa.html' title='Jak w zasadzie działają moduły flexa'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-6564291378328945307</id><published>2010-09-16T01:29:00.001-07:00</published><updated>2010-09-27T03:45:37.261-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Projekt'/><category scheme='http://www.blogger.com/atom/ns#' term='Zarządzanie'/><title type='text'>Jak uwalić projekt - Epilog</title><content type='html'>Ponieważ minęło trochę czasu, postanowiłem napisać obiecany epilog do poprzedniej historii.&lt;br /&gt;&lt;br /&gt;Firma odkryła, że jest jakiś problem i zatrudniała Audytora. Audytor zajął się oglądaniem jak są prowadzone projekty, głównie na poziomie zarządzania. Audyt zajął około miesiąca i obejmował głównie rozmowy z project managerami i dyrektorem technicznym. Po miesiącu Audytor został nowym dyrektorem technicznym, natomiast dyrektor techniczny został architektem systemu (który przecież trwa już od ponad roku).&lt;br /&gt;Projekt w tym czasie spokojnie sam posuwał się do przodu. Kierownictwo było zajęte głównie roszadami oraz przygotowywaniem papierków, negocjacjami z klientem oraz audytorem/nowym dyrektorem. Oznacza to, że tak na prawdę, nikt nie kontrolował jak idą prace. Co prawda programiści są kompetentni, więc tyle ile mogą robią, ale mimo wszystko kompletny brak kontroli jest nieco niepokojący.&lt;br /&gt;&lt;br /&gt;W między czasie, zaczeło wychodzić (przynajmniej w gronie programistów), że nowi programiści (zdalni) w zasadzie niewiele wnoszą do projektu, a dodatkowo poświeca się im sporo czasu. Mimo to, zawsze można jeszcze pod-zlecić innej firmie część prac. O ile osobiście uważam, że jest to całkiem niezły pomysł, to w sytuacji w której dokumentacja jest na poziomie niskim (nie zgodnym z założeniami, nie dokładnym, nie aktualnym, itd.), nie da się ani dobrze oszacować czasu, ani nie da się po prostu pod-zlecić. Trzeba dodatkowo dać kogoś, w zasadzie na pełen etat kto będzie wyjaśniał wątpliwości. Mimo to, pod-zlecanie stało się nowym motto firmy. Żeby było lepiej, to zawsze można wziąć firmę po znajomości (a nie koniecznie po kwalifikacjach jej pracowników).&lt;br /&gt;&lt;br /&gt;Miesiąc po objęciu stanowiska przez nowego dyrektora zaczęły się ruchy w poszukiwaniu czasu programistów. Pojawiły się duże znaki zapytania odnośnie urlopów oraz prośby zaplanowania go na później jeżeli tylko się da. Pojwaiły się również spotkania personalne z dyrektorem w zakresie: Co robisz, ile ci to jeszcze zajmie, kiedy planujesz urlop i jak ma on się do tego co masz zaplanowane do zrobienia. Mam jednak wrażenie, że mimo dobrych chęci, nie wnosiły one niczego w projekt. Oczywiście część programistów zajmowała się tylko komunikacją z zleceniobiorcami (w tym programistami zewnętrznymi) więc nie zajmowali się programowaniem. Co interesujące, dzięki temu ruchowi, prace spowolniły tempo.&lt;br /&gt;&lt;br /&gt;Mniejwięcej w tamtym okresie sprawy przybrały jeszcze gorszy obrót, bo okazało się, że dwóch najbardziej doświadczonych programistów stwierdziło, że odchodzi. Ponieważ jeden miał na głowie bezpieczeństwo aplikacji a drugi silnik, to rozpoczęto działania w celu przekazania zadań związanych z silnikiem. Przekazanie bezpieczeństwa zostawiono na 2 ostatnie dni pracy. W końcu ten autorski rozbudowany system spełniający masę wymagań (często na wyrost) na pewno łatwo można przekazać...&lt;br /&gt;&lt;br /&gt;Mniejwięcej w tamtym okresie okazało się również, że firma oficjalnie zawiesza projekt, ze względu na brak umówionej płatności ze strony klienta. W zasadzie, można by przyjąć, że to już był koniec projektu. Oczywiście firma planowała jeszcze wywiązać się ze zobowiązań kontynuując projekt wewnętrznie, ale oficjalnie projekt był zawieszony.&lt;br /&gt;Klient oczywiście chciał, żeby projekt był kontynuowany, ale z punktu widzenia firmy, lepsza była  ścieżka sądowa.&lt;br /&gt;&lt;br /&gt;Na tym etapie rozpoczęto również zrywanie umów z podwykonawcami (czyli po około miesiącu).&lt;br /&gt;&lt;br /&gt;Stan: Realizacja projektu w miarę obiektywnie 75% (ale pewne problemy nie zostały w ogóle podjęte), wg firmy 90 - 95%. Oficjalnie zawieszony.&lt;br /&gt;Zespół: 3 programistów 2 osoby od kontaktów.&lt;br /&gt;[EDIT] Zgodnie z komentarzem. Brak testów obciążeniowych, a funkcjonalność ta co jest, przetestowana pobieżnie, przez testerów którzy nie znają się na pisaniu testów i programowaniu (czytaj: Osoby które przeklikają aplikację).&lt;br /&gt;&lt;br /&gt;Ta zupełnie teoretyczna historia mogła się wydarzyć na prawdę. Takich firm w Polsce jest wiele. Nie każdy, mimo tego, że potrafi wykonać mały produkt, powinien brać się za realizację dużych projektów. Dodatkowo, warto jednak zatrudnić specjalistów, w dziedzinach które dla dużych projektów są istotne, takich jak analitycy, architekci, specjaliści z danych dziedzin. Małe projekty nie wymagają tak silnego zaplecza. Można je realizować znacznie luźniej, uzupełniając pewne elementy w biegu. W przypadku dużych projektów, błędy w architekturze, dokumentacji, zarządzaniu kosztują firmy na prawdę duże pieniądze.&lt;br /&gt;&lt;br /&gt;PS. Pozdrowienia dla tych, którzy jednak czytają tego bloga :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-6564291378328945307?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/6564291378328945307/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2010/09/jak-uwalic-projekt-epilog.html#comment-form' title='Komentarze (2)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/6564291378328945307'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/6564291378328945307'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2010/09/jak-uwalic-projekt-epilog.html' title='Jak uwalić projekt - Epilog'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-8624423735227249340</id><published>2010-05-06T00:19:00.000-07:00</published><updated>2010-05-06T00:36:54.722-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Problem'/><category scheme='http://www.blogger.com/atom/ns#' term='60Hz'/><category scheme='http://www.blogger.com/atom/ns#' term='LCD'/><category scheme='http://www.blogger.com/atom/ns#' term='59Hz'/><category scheme='http://www.blogger.com/atom/ns#' term='Monitor'/><title type='text'>Problem z 59Hz</title><content type='html'>Od 3 lat posiadam dobry moniotr LCD. Do tej pory nie było z nim problemów.. aż do czasu. Zmieniłem komputer, a ponieważ musiałem oszczędzić na karcie graficznej to kupiłem najtańszą, ale taką która obsłuży rozdzielczość 1900x1200. Niestety okazało się to nie wystarczające. Co prawda karta obsługuje tą rozdzielczość, ale okazuje się, że pod Windows 7 (i z tego co czytałem również w Vista) są dość ciekawe sterowniki dla monitorów.&lt;br /&gt;&lt;br /&gt;Windows źle rozpoznaje częstotliwość podawaną przez monitor. Część monitorów LCD pracuje w częstotliwości 59.94. Niestety Windows nie rozpoznaje ich jaką tą częstotliwość tylko 59Hz. O ile dla zwykłego monitora nie stanowi to problemu, o tyle dla LCD powoduje brak synchronizacji. Dla złącz DVI powoduje brak ekranu (lub jego pojawianie się i znikanie (na przykład 1s jest.. 10s nie ma), dla złącza VGA powoduje migotanie lub pasy na ekranie.&lt;br /&gt;&lt;br /&gt;Problem jest taki, że na liście obsługiwanych częstotliwości mam 59 i 60Hz, ale nigdy nie można ustawić 60Hz (które jest prawidłowe), ponieważ monitor zgłasza że obsługuje 59.94 (a więc nie 60).&lt;br /&gt;&lt;br /&gt;Microsoft temat zamknął określając że wszystko jest w porządku (http://support.microsoft.com/kb/2006076).&lt;br /&gt;&lt;br /&gt;Rozwiązania:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Zmiana karty graficznej - Tak wiem... teoretycznie problem z monitorem, ale w praktyce monitor już wcześniej pracował prawidłowo z moim Laptopem, a po zmianie karty graficznej problem zniknął. (Poprzednia karta jest w porządku, z monitorem kineskopowym działa ok)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Powerstrip - program pozwala na dość mocne sterowanie kartą graficzną (w tym częstotliwościami dla monitora)&lt;/li&gt;&lt;li&gt;Można próbować definiować własne rozdzielczości, ale u mnie nie zdało to egzaminu.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-8624423735227249340?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/8624423735227249340/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2010/05/problem-z-59hz.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/8624423735227249340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/8624423735227249340'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2010/05/problem-z-59hz.html' title='Problem z 59Hz'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-6703465775208275861</id><published>2010-04-28T09:35:00.000-07:00</published><updated>2010-04-28T11:21:01.071-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Projekt'/><category scheme='http://www.blogger.com/atom/ns#' term='Zarządzanie'/><title type='text'>Jak uwalić projekt informatyczny za grube pieniądze</title><content type='html'>&lt;div style="text-align: left;"&gt;Na usta cisną mi się tylko słowa Grega Zorby "Jaka piękna katastrofa" , ale wybiegam odrobinę w przyszłość.&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Postanowiłem się podzielić z szerszą publiką moimi przemyśleniami na temat zarządzania projektami informatycznymi, a przede wszystkim, jak nie powinno się prowadzić projektu. Ponieważ obowiązuje mnie tajemnica, będę się starał unikać szczegółów, a pewne fakty mogą zostać przeze mnie zmienione.&lt;br /&gt;&lt;br /&gt;Uczestniczę obecnie w projekcie który do małych nie należy a jego wartość wyceniona została na sumę, której w LOTTO z reguły wygrać się nie da. Projekt zawiera bardzo wiele powiązanych ze sobą systemów, zaczynając od CRM poprzez CMS, GIS, oraz zarządzanie bazą danych (w tym graficzną jej reprezentację) z poziomu GUI. Aplikacja pozwala na pracę w przeważającej części za pomocą przeglądarki. Z samego zakresu technologii, jak również jego wartości jasno wynika, że projekt do małych nie należy. Jak na projekt tej wielkości przystało, projekt jest zarządzany zgodnie z jedną z bardziej popularnych metodyk (jednak tylko na styku z klientem). Czas realizacji: około 1,5 roku + utrzymanie.&lt;br /&gt;&lt;br /&gt;A teraz prosty przepis, jak taki projekt uwalić:&lt;br /&gt;Na początek mamy &lt;span style="font-weight: bold;"&gt;podpisanie umowy&lt;/span&gt;, w ramach której obiecujemy klientowi gruszki na wierzbie. Przy czym, klient sam nie do końca wie co mu jest potrzebne, więc wymaga jak najwięcej się da. Nie ma przy tym sensu sprawdzenie, czy wymagania w ogóle mają sens, lub czy się wzajemnie nie wykluczają. Klient w końcu nigdy w życiu nie dał by sprzecznych ze sobą wytycznych, nawet jeżeli jest kilkanaście osób które ze strony klienta mogą decydować i wspólnie podejmować decyzje. Dodatkowo, ponieważ klient wymaga wstępnego projektu, można wziąć projekt innego systemu i trochę przystosować. Ponad dwustu tabel w dokumentacji bazy danych i tak nikt nie będzie czytał&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;. W zasadzie, jak się głębiej nad tym zastanowić. To te tabele można żywcem wziąć, po co to przystosowywać, dopisać tylko do tego jakąś otoczkę.&lt;br /&gt;&lt;br /&gt;Kolejnym krokiem jest &lt;span style="font-weight: bold;"&gt;zebranie wymagań&lt;/span&gt;. Ten krok można w zasadzie pominąć, bo przecież wymagania zostały już w zasadzie zebrane w ramach umowy, a tylko doprecyzować jak to w zasadzie ma być w ramach analizy.&lt;br /&gt;&lt;br /&gt;A więc przystępujemy do &lt;span style="font-weight: bold;"&gt;analizy&lt;/span&gt;. Oczywiste jest, że w tej fazie, osoby zbierające wymagania nie muszą mieć ani doświadczenia, ani śladowego wykształcenia informatycznego. W końcu system który będzie wykonywany można zaplanować całkowicie w oderwaniu od sprzętu czy oprogramowania. Jeżeli przypadkiem okazuje się, że nie można do końca zebrać wymagań, to należy w dokumentacji wpisać jakieś ogólniki - uzupełni się w ramach projektu technicznego. Ponieważ zespól zbierający te informacje nie musi być duży, w zasadzie wystarczy 5 osób, to może się okazać, że będzie problem z dotrzymaniem terminów. Na szczęście, klient wymaga więcej, niż było podpisane w umowie, w związku z czym można podpisać aneks do analizy. Osób co prawda więcej nie będzie, ale zawsze można dłużej pisać. Klient niestety jest cwańszy, bo co prawda zgadza się na przesunięcie terminu oddania analizy (z aneksem), ale nie pozwala na przesunięcie terminu oddania projektu technicznego, którego realizacja ma być wykonana w terminie, a co najwyżej zostanie uzupełniony później o aneks. W ten oto sposób, aneks zostaje ze sporymi bólami oddany w terminie, co wypada w połowie czasu przewidzianego na oddanie projektu technicznego.&lt;br /&gt;&lt;br /&gt;Stan:&lt;br /&gt;Połowa czasu przewidzianego na projekt techniczny.&lt;br /&gt;Analiza - po łepkach, nie kompletna a miejscami sprzeczna.&lt;br /&gt;Aneks do analizy - tak samo&lt;br /&gt;Projekt Techniczny -brak&lt;br /&gt;&lt;br /&gt;A więc teraz można przystąpić do realizacji &lt;span style="font-weight: bold;"&gt;projektu technicznego&lt;/span&gt;. Najlepiej jak go zrobi  jedna osoba techniczna. Osoba techniczna, to zresztą odrobinę nadużycie semantyczne. w/w osoba, to programista, z 3,5 letnim doświadczeniem, nawet dość szerokim, ale twierdzący, że jego najsłabszą stroną są bazy danych. A więc najlepiej będzie, jeżeli to właśnie ta osoba zaprojektuje bazę danych. Jednak, aby trochę życie ułatwić część bazy danych już jest gotowa i nie należy jej dotykać. Wynika to z faktu, że są gotowe narzędzie wypełniające tą bazę. Co prawda nikt tak na prawdę nie wie jakie to narzędzia, w jakim języku są napisane ani czy ta struktura która jest to właśnie z tych narzędzi, ale są. Oczywiście programista kompletnie nie jest wprowadzony w tematykę GIS, na której całość aplikacji się opiera. Wszystkie osoby które mają jakiekolwiek pojęcie są oczywiście zajęte pisaniem aneksu do analizy, a programista ma wytyczne "zrób projekt techniczny, tam jest analiza". W razie pytań dostaje odpowiedź "wszystko jest w analizie", co oczywiście załatwia sprawę. Po zakończeniu aneksu do analizy (przypominam w połowie trwania projektu technicznego), okazuje się, że w sumie nie wiadomo dlaczego, ale projekt techniczny jest w lesie, klient dodatkowo poza modelem danych wymaga jeszcze projektu interfejsów oraz przypadków użycia, a dodatkowo to co jest, nie jest zgodne z aneksem do analizy, który przecież już od 3 dni leży w repozytorium. Programista zostaje należycie opieprzony za jego niekompetencję a wiec sprawa jest załatwiona. Wprawiony team od analizy siada i wspólnymi siłami oraz posiłkując się dokumentacją z poprzedniego projektu generuje setki stron dokumentów technicznych. Co prawda są pewne zgrzyty, bo okazuje się, że chyba jednak nie wszystko jest w analizie, główny standard wymiany danych jest nie dopracowany (mimo, że zatwierdzony przez klienta). W związku z tym pewne elementy nie zostają doprecyzowane. Ponieważ termin realizacji projektu technicznego stoi pod wielkim znakiem zapytania atmosfera jest bardzo nieprzyjemna i napięta, ale praca wre.&lt;br /&gt;Wreszcie następuje przełom. Okazuje się, że klient zgodził się na rozszerzenie. Oznacza to, że mamy 1,5 miesiąca więcej na wykonanie projektu technicznego. Co prawda klient wymaga teraz trochę więcej i w ramach rozszerzenia przemyca pewne funkcjonalności, ale mimo to są szanse się wyrobić, szczególnie, że projekt interfejsów i przypadki użycia są w powijakach. Jest jednak mały haczyk, polegający na tym, że równolegle należy rozpocząć już implementację, bo projekt techniczny do rozszerzenia idzie jakby torem obok.&lt;br /&gt;&lt;br /&gt;To jest dobry moment na rozpoczęcie &lt;span style="font-weight: bold;"&gt;rekrutacji zespołu&lt;/span&gt; który ten projekt zrealizuje. A więc na dobry początek należy stwierdzić ile osób potrzeba. Ponieważ mamy kupę czasu (i nie są znane pełne wymagania), programista szacuje, że potrzeba około 6 osób od zaraz, do samej części CRM, CMS aby projekt zrealizować, około 5 do GIS. Oczywiście programista nie wie co mówi, ale rekrutacja się rozpoczyna. Po dwóch miesiącach rekrutacji są w zespole programistycznym już 3 osoby (w tym programista, który cały czas zajmuje się projektem technicznym). Od tego momentu, programista staje się team leaderem, ponieważ jest osobą techniczną która ma najbardziej całościowy pogląd na cały projekt (i jedyną która ma jakikolwiek pogląd od strony technicznej).&lt;br /&gt;&lt;br /&gt;A więc po 2 miesiącach stan jest taki, że zostaje wykonany szkielet aplikacji oraz wypracowane zostają standardy kodowania w wybranych (nowych) technologiach. Warto nadmienić, że cały zespół części technologii uczy się od zera. W końcu jak by przynajmniej jedna osoba znała technologię....&lt;br /&gt;&lt;br /&gt;Myślę, że to jest moment, w którym możemy określić, że tak na prawdę (z 2 miesięcznym opóźnieniem) rozpoczęła się &lt;span style="font-weight: bold;"&gt;faza implementacji&lt;/span&gt;.&lt;br /&gt;Status:&lt;br /&gt;2 miesiące do tyłu&lt;br /&gt;3 osoby w zespole (zamiast 6).&lt;br /&gt;0 osób w zespole GIS (zamiast 5).&lt;br /&gt;Projekt techniczny bazujący na kulawej analizie wykonany przez zbyt małą  grupę ludzi nie mających o tym zielonego pojęcia. - Sumarycznie 1,5 tyś  stron.&lt;br /&gt;Projekty Interfejsów taki, że każdy interfejs nie jest powiązany z żadnym innym (i nie przystaje do technologii).&lt;br /&gt;Przypadki Użycia w dużej części nie są powiązane z interfejsami a dodatkowo część z nich jest pozostałością z innego systemu.&lt;br /&gt;&lt;br /&gt;Ponownie zostaje zadane pytanie ile osób potrzeba - odpowiedź - na dzień dzisiejszy 7 w samym zespole do CRM, CMS. Na zapotrzebowanie do innych zespołów nie ma odpowiedzi, ponieważ prace nie są jeszcze na ten moment zaplanowane. Miesiąc później zespół liczy już 5 osób. Liczba ta utrzymuje się przez kolejne miesiące. Kierownictwo ma coraz większe zastrzeżenia do opóźnień i wielokrotnie stara się otrzymać od zespołu informację kiedy nadgonią opóźnienia. Warto nadmienić, że żadne nadgodziny, a przynajmniej płatne nie wchodzą w grę.&lt;br /&gt;&lt;br /&gt;Mijają 3 miesiące i okazuje się, że jednak opóźnienia zamiast maleć rosną. Winny jest team leader który nie potrafi doprowadzić do zmniejszenia opóźnienia. Drań jeden dodatkowo próbuje się bronić, mówiąc, że od początku mówił, że potrzeba 7 osób a nie 5. W zespole od GIS również nie dzieje się dobrze. Zespół nie dość, że jest między dwoma projektami, to jeszcze wredny team leader zespołu GIS postanowił odejść z pracy. Coś mówił o burdelu w zarządzaniu, wymagał jakiś procedur i nie pozwalał na to, żeby jednej osobie zlecało pracę 5 osób w ciągu jednego dnia, ale szefostwo wie, że i tak tylko wprowadzał niepotrzebny zamęt w projekcie i w sumie lepiej że go nie będzie.&lt;br /&gt;&lt;br /&gt;Mijają 2 tygodnie. Klient zaczyna się niepokoić stanem prac i twierdzi, że jak prace nie zostaną nadgonione, to będzie trzeba wprowadzić plan naprawczy. Zarząd, a za nim kierownictwo projektu nie zgadzają się jednak na to, żeby już wprowadzać plan naprawczy, w związku z czym zespół dostaje polecenie zrobić tak, żeby następna prezentacja aplikacji przeszła bez problemów. Team leader informuje kierownictwo, że jedyną możliwością aby to zrobić, to będzie zrobienie kompletnej wydmuszki (mockupu), już nie tak jak do tej pory, że jest masa błędów, ale generalnie jest, tylko całkowitej wydmuszki. Propozycja zostaje przyjęta.&lt;br /&gt;&lt;br /&gt;Mijają 3 tygodnie. Udało się. Klient jest zadowolony ze zwiększonych postępów pracy. Co prawda w czasie prezentacji następuje spora awaria bazy danych więc nie wszystkie funkcjonalności można pokazać, ale ogólnie prezentacja kończy się sukcesem. Co prawda programiści zostają przedstawieni jako banda niekompetentnych... (oszczędzę sobie szczegółów), ale prezentacja przeszła dobrze.&lt;br /&gt;&lt;br /&gt;Z bliżej nie określonych powodów w następnym tygodniu prace jakoś nie idą za szybko.&lt;br /&gt;&lt;br /&gt;Są decyzje. Teraz już nie ukrywamy problemów. Można zacząć naprawiać błędy.&lt;br /&gt;&lt;br /&gt;Minął kolejny miesiąc. Prace co prawda nie postępują, ale aplikacja zaczyna powoli działać. Zaczynają się co prawda problemy z określeniem postępów prac. Tym razem przy wyznaczaniu harmonogramu prac nikt już się nie konsultuje z team leaderem, bo w końcu tak spieprzył poprzedni harmonogram prac, że nie ma to sensu. Aby zapewnić pełną kontrolę nad źle zarządzanym projektem, tematem zajmuje się od tej pory project manager. Co prawda team leader niby nadal jest team leaderem, ale wszelkie podziały prac i decyzje są dokonywane poza nim.&lt;br /&gt;&lt;br /&gt;2 tygodnie później, po ostrej awanturze za powolne wykonywanie obowiązków programisty, team leader (teraz już 7 osobowego zespołu) rezygnuje ze swojego stanowiska. Nikt inny nie zostaje wyznaczony do tej (nikomu nie potrzebnej) roli.&lt;br /&gt;&lt;br /&gt;Stan na dzień:&lt;br /&gt;-2 miesiące względem pierwotnego harmonogramu.&lt;br /&gt;- +0 miesięcy względem harmonogramu z planu naprawczego (przy czym wg. starego team leadera już pierwszy miesiąc jest niewykonalny, ale jego nikt o zdanie nie pyta)&lt;br /&gt;-7 osoób w zespole, z czego 2 osoby mają pracować zdalnie&lt;br /&gt;- brak team leadera&lt;br /&gt;- brak nałożonych kar ze strony klienta (ale są już odpowiednie zapisy odnośnie planu naprawczego)&lt;br /&gt;&lt;br /&gt;C.D.N.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-6703465775208275861?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/6703465775208275861/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2010/04/jak-uwalic-projekt-informatyczny-za.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/6703465775208275861'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/6703465775208275861'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2010/04/jak-uwalic-projekt-informatyczny-za.html' title='Jak uwalić projekt informatyczny za grube pieniądze'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-987349566548017311</id><published>2009-07-15T05:05:00.000-07:00</published><updated>2009-07-15T05:08:51.172-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><title type='text'>Zaznaczanie kawałka tekstu w dla pola input=text</title><content type='html'>O ile problem nie jest specjalnie nowy, o tyle znalezienie rozwiązania stanowiło dla mnie spore wyzwanie. W pierwszym momencie uznałem nawet, że javascript po prostu nie daje takich możliwości. Dopiero gdy postanowiłem znaleźć jak się ustawia kursor w dowolnym miejscu pola input, okazało się, że zaznaczanie tekstu również jest dość proste. Oto rozwiązanie:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;if (input.createTextRange) {&lt;br /&gt;    var range = input.createTextRange();&lt;br /&gt;    range.collapse(true);&lt;br /&gt;    range.moveEnd('character', selectionEnd);&lt;br /&gt;    range.moveStart('character', selectionStart);&lt;br /&gt;    range.select();&lt;br /&gt;  }&lt;br /&gt;  else if (input.setSelectionRange) {&lt;br /&gt;    input.focus();&lt;br /&gt;    input.setSelectionRange(selectionStart, selectionEnd);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Rozwiązanie zaczerpnięte ze strony http://faqts.com/knowledge_base/view.phtml/aid/13562&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-987349566548017311?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/987349566548017311/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2009/07/zaznaczanie-kawaka-tekstu-w-dla-pola.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/987349566548017311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/987349566548017311'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2009/07/zaznaczanie-kawaka-tekstu-w-dla-pola.html' title='Zaznaczanie kawałka tekstu w dla pola input=text'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-5676704422832928043</id><published>2009-06-29T01:51:00.000-07:00</published><updated>2009-06-29T02:14:38.038-07:00</updated><title type='text'>Piractwo komputerowe</title><content type='html'>W ostatnim czasie pojawiło się sporo aresztowań związanych z piractwem. Ofiarami tego procederu są głównie studenci, chociaż nie tylko. Ponieważ jest to blog dotyczący programowania, postanowiłem przedstawić swój punkt widzenia związany z piractwem.&lt;br /&gt;&lt;br /&gt;Tak na prawdę, mamy kilka grup pozyskujących oprogramowanie:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Osoby które pobierają oprogramowanie nielegalnie, i nie zrobiły by inaczej.&lt;/li&gt;&lt;li&gt;Osoby które pobierają oprogramowanie nielegalnie, bo tak jest prościej.&lt;/li&gt;&lt;li&gt;Osoby które pobierają oprogramowanie nielegalnie, bo nie stać ich aby pozyskać je inaczej.&lt;/li&gt;&lt;li&gt;Osoby które kupują oprogramowanie, bo nie potrafiły złamać zabezpieczeń&lt;/li&gt;&lt;li&gt;Osoby które kupują oprogramowanie, bo uważają, że twórca powinien dostać wynagrodzenie za swój trud.&lt;/li&gt;&lt;/ul&gt;W zasadzie nie popieram piractwa. Mówię w zasadzie, ponieważ mój punkt widzenia jest uzależniony od tego, o której grupie mówię.&lt;br /&gt;&lt;br /&gt;Jeżeli mówimy o grupie pierwszej, do której należą osoby które celowo pobierają oprogramowanie, łamią zabezpieczenia i na przykład sprzedają je dalej, tak aby osiągnąć z tego powodu korzyści, to uważam, że jest to postępowanie karygodne i należy je tępić.&lt;br /&gt;&lt;br /&gt;Jeżeli mówimy o osobach wygodnych, to jest to kwestia złej polityki firm wydających oprogramowanie. Nie mówię tutaj o zaostrzeniu polityki bezpieczeństwa, tylko o złych zasadach udostępniania oprogramowania. Jeżeli ktoś z tej grupy ma możliwość pobrania za darmo oprogramowania z ograniczoną funkcjonalnością, to istnieje duża szansa, że takie oprogramowanie pobierze. Dodatkowo, jeżeli w przyszłości będzie potrzebował pełnej funkcjonalności, to istnieje duża szansa, że to oprogramowanie zakupi.&lt;br /&gt;&lt;br /&gt;W przypadku osób których nie stać, to są to osoby, które i tak by nie zakupiły naszego oprogramowania i ściganie ich, tak na prawdę nic nie zmienia, a dodatkowo psuje wizerunek naszej firmie. Co komu zawinił biedny student, że się nasyła na niego policję i ze sporym prawdopodobieństwem rujnuje dalsze życie? Ten student i tak by oprogramowania nie zakupił, a co za tym idzie, nie poniesiemy żadnej straty z tytułu tego, że pobierze on nasze oprogramowanie. Warto przygotować dla takiego rynku specjalną ofertę, ale tak na prawdę ściganie mija się z celem.&lt;br /&gt;&lt;br /&gt;Dla nie potrafiących złamać zabezpieczenia. Cóż, podstawowe zabezpieczenia w aplikacji warto stosować, właśnie ze względu na tą grupę. Natomiast bardziej zaawansowane algorytmy mijają się z celem. Zastosowanie bardziej skomplikowanych algorytmów spowoduje tylko większe zainteresowanie hakerów pragnących wyzwań, i w ten sposób powstanie w miarę łatwo dostępny crack do naszych zabezpieczeń. Po co dawać takie wyzwanie? Lepsza jest dobra polityka licencyjna dająca dostęp do produktu, nawet jeżeli z biznesowego punktu widzenia nie koniecznie ma to sens.&lt;br /&gt;&lt;br /&gt;Dla pozostałej grupy należą się podziękowania za uczciwość. Dzięki tej grupie zarówno biznes oprogramowania jak i muzyczno-filmowy nadal może się rozwijać.&lt;br /&gt;&lt;br /&gt;Do tego wszystkiego dochodzi jeszcze kwestia samych zabezpieczeń, które osiągnęły poziom, przy którym wgrywa się cracka tylko po to, żeby łatwiej się korzystało z produktu. Mam kilku znajomych, którzy kupują oryginalne oprogramowanie, a następnie wgrywają cracki, bo na przykład nie chce im się za każdym razem jak chcą pograć w grę, szukać płyty.&lt;br /&gt;&lt;br /&gt;Myślę że swój punkt widzenia przedstawiłem. Jestem przeciwny piractwu, ale jestem jednocześnie przeciwny idiotycznym politykom licencyjnym oraz ściganiem ludzi którzy nigdy nie nabyli i nie mogli nabyć danego oprogramowania.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-5676704422832928043?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/5676704422832928043/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2009/06/piractwo-komputerowe.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/5676704422832928043'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/5676704422832928043'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2009/06/piractwo-komputerowe.html' title='Piractwo komputerowe'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-3595030610967124901</id><published>2009-05-18T04:53:00.000-07:00</published><updated>2009-05-18T05:25:50.472-07:00</updated><title type='text'>Hibernate, dziwne zachowanie InheritanceType.SINGLE_TABLE</title><content type='html'>Dziś trafiłem na bardzo ciekawy problem. Korzystałem z obiektów, z dziedziczeniem oraz mapowaniem z wykorzystaniem pojedyńczej tabeli. Tego typu rozwiązanie ma tą zaletę, że jest szybkie, oraz tą wadę, że potrzeba trochę więcej miejsca w bazie. Dla mnie to drugie nie było problemem w tej konkretnej sytuacji, a ponieważ zależało mi na szybkości, to ta metoda na pozór wyglądała bardzo ciekawie.&lt;br /&gt;&lt;br /&gt;W skrócie moje klasy wyglądały tak:&lt;br /&gt;&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;@Entity&lt;br /&gt;@Inheritance(strategy=InheritanceType.SINGLE_TABLE)&lt;br /&gt;@DiscriminatorValue("PLAYER_SHORT_INFO")&lt;br /&gt;public class PlayerShortInfo implements IsSerializable {&lt;br /&gt;&lt;br /&gt;    @Id @GeneratedValue&lt;br /&gt;    @Column(name = "ID", unique = true, nullable = false)&lt;br /&gt;    private long id;&lt;br /&gt;[...]&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@Entity&lt;br /&gt;@DiscriminatorValue("PLAYER_INFO")&lt;br /&gt;@PrimaryKeyJoinColumn(name="ID")&lt;br /&gt;public class PlayerInfo extends PlayerShortInfo implements IsSerializable {&lt;br /&gt;@ManyToOne(optional = false, cascade = {CascadeType.REFRESH, CascadeType.PERSIST}, fetch = FetchType.EAGER)&lt;br /&gt;PlayerType type;&lt;br /&gt;[...]&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Nigdy nie tworzyłem obiektów klasy PlayerShortInfo.Dziedziczneie było zrobione głownie dla ograniczenie ilości danych przesylanych klientowi, gdy potrzebuje tylko danych skróconych (ale na przykład nie dla jednego obiektu, a dla 100). Takie rozwiązanie wydało mi sie proste i wydajne. Niestety, dziś okazało sie inaczej.&lt;br /&gt;&lt;br /&gt;Klasa &lt;i&gt;PlayerInfo&lt;/i&gt;, jest klasa która nigdy nie jest przekazywana do warstwy WEB. Jest wykorzystywana tylko w logice biznesowej, a wiec nawet nie było potrzeby, aby implementowała IsSerializable (wymagane przez GWT). W czasie testów, wyszło jednak, ze GWT nie może przetransportować listy zawierającej &lt;i&gt;PlayerShortInfo&lt;/i&gt; ponieważ PlayerType (!!!) nie implementuje IsSerializable. Po sprawdzeniu, okazało sie, ze zeczywiscie nie implementuje, tylko ze &lt;i&gt;PlayerType&lt;/i&gt; nigdy nie miał być przesyłany do warstwy WEB. &lt;br /&gt;&lt;br /&gt;Po dłuższych poszukiwaniach, okazało sie ze SELECT: &lt;i&gt;(List&lt;PlayerShortInfo&gt;)em.createQuery("select d from PlayerShortInfo d order by id").getResultList();&lt;/i&gt; zwraca listę nie &lt;i&gt;PlayerShortInfo&lt;/i&gt; ale &lt;i&gt;PlayerInfo&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Wnioski:&lt;br /&gt;InheritanceType.SINGLE_TABLE jest nieco mylące. Obiektem ładowanym z bazy jest zawsze obiekt który został zapisany, nawet jeżeli w Select jest jasno podane ze chcemy załadować obiekt wyższego rzędu. Co sie dzieje, jest wykonywane załadowanie obiektu który został zapisany a następnie rzutowanie go na element docelowy. Sprawa jest bardziej skomplikowana, bo jeżeli dziecko jest obiektem ciężkim, to pomimo ze chcemy załadować lekkiego rodzica, załadowany zostanie ciężki obiekt dziecka, razem ze wszystkimi atrybutami które ono posiada (na przykład listami innych obiektów).&lt;br /&gt;&lt;br /&gt;Rozwiązanie:&lt;br /&gt;Moim zdaniem nie ma dobrego rozwiązania. Trzeba być świadomym problemu i stosować takie rozwiązania w swojej aplikacji, aby nie było potrzeby stosowania tego rodzaju dziedziczenia.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-3595030610967124901?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/3595030610967124901/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2009/05/hibernate-dziwne-zachowanie.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/3595030610967124901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/3595030610967124901'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2009/05/hibernate-dziwne-zachowanie.html' title='Hibernate, dziwne zachowanie InheritanceType.SINGLE_TABLE'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-6677299360023166380</id><published>2009-05-11T02:22:00.001-07:00</published><updated>2009-05-11T02:56:41.707-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Wydajność'/><category scheme='http://www.blogger.com/atom/ns#' term='GWT'/><title type='text'>Duża ilość elementów w GWT</title><content type='html'>GWT generalnie jest fajną platformą, ale niestety sam javascript nie jest najszybszy. Okazuje się, że dodanie dużej ilości elementów załadowanych z bazy i wstawienie ich do tabeli stanowi spory problem. Na przykład, ładując 500 elementów i wstawiając je do &lt;span style="font-style: italic;"&gt;FlexTable&lt;/span&gt; albo do &lt;span style="font-style: italic;"&gt;Grid&lt;/span&gt;, strona ładuje się dość długo, szczególnie na słabszych komputerach. Ponieważ zależało mi na szybkości ładowania elementów, postanowiłem temat zbadać i spróbować coś z nim zrobić.&lt;br /&gt;&lt;br /&gt;Zarówno &lt;span style="font-style: italic;"&gt;Grid&lt;/span&gt; jak i &lt;span style="font-style: italic;"&gt;FlexTable&lt;/span&gt;, a w zasadzie również &lt;span style="font-style: italic;"&gt;VerticalPanel&lt;/span&gt; oraz &lt;span style="font-style: italic;"&gt;HorizontalPanel&lt;/span&gt; są oparte o tablice. Tablica jako taka jest elementem dość skomplikowanym w budowie, a dodatkowo GWT dodaje dość dużo kodu sprawdzającego, warunkowego oraz debugu. Cały ten kod powoduje, że Całość działa o wiele wolniej.&lt;br /&gt;&lt;br /&gt;Ponieważ efekt zbliżony do tabeli można osiągnąć również przy pomocy arkusza styli CSS oraz zwykłych div, postanwoiłem spróbować przerobić standardową tabelę na pseudotabelę zrobioną przy pomocy &lt;span style="font-style: italic;"&gt;DIV&lt;/span&gt; z &lt;span style="font-style: italic;"&gt;flow:left; &lt;/span&gt;GWT posiada kilka elementów które mi na to pozwalają. Pierwszy z nich to &lt;span style="font-style: italic;"&gt;FlowPanel&lt;/span&gt; który jest tłumaczony na DIV i pozwala na dodanie innych komponentów do środka. Drugi to Label, SimplePanel oraz Hyperlink pozwalające na dodanie innych elementów.&lt;br /&gt;&lt;br /&gt;Są dwie zasady jeżeli chcemy osiągnąć efekt właściwy.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Wszystkie elementy muszą mieć na stałe ustawioną wysokośc oraz szerokość&lt;/li&gt;&lt;li&gt;Sumaryczna szerokość elemtnów wewnątrz "tabeli" musi być równa szerokości "tabeli"&lt;/li&gt;&lt;/ul&gt;Wygenerowany kod będzie wyglądał tak:&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div style="width:200px;display:block;"&amp;gt;&lt;br /&gt;   &amp;lt;div style="float:left;width:100px;height:20px;"&amp;gt;Nazwa 1&amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;div style="float:left;width:100px;height:20px;"&amp;gt;Wartosc 1&amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;div style="float:left;width:100px;height:20px;"&amp;gt;Nazwa 2&amp;lt;/div&amp;gt;&lt;br /&gt;   &amp;lt;div style="float:left;width:100px;height:20px;"&amp;gt;Wartosc 2&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;  &lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Efekt wyglada tak:&lt;br /&gt;&lt;div style="width: 200px; display: block;"&gt;&lt;div style="float: left; width: 100px; height: 20px;"&gt;Nazwa 1&lt;/div&gt;&lt;div style="float: left; width: 100px; height: 20px;"&gt;Wartosc 1&lt;/div&gt;&lt;div style="float: left; width: 100px; height: 20px;"&gt;Nazwa 2&lt;/div&gt;&lt;div style="float: left; width: 100px; height: 20px;"&gt;Wartosc 2&lt;/div&gt;&lt;/div&gt;  &lt;br /&gt;&lt;br /&gt;Z wygladu mamy tabele, a wiec to o co chodzi.&lt;br /&gt;&lt;br /&gt;To samo mozna osiagnac w GWT, tylko style nalezy przeniesc do arkusza styli.&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;FlowPanel content = new FlowPanel();&lt;br /&gt;Label l1 = new Label("Nazwa 1");&lt;br /&gt;Label w1 = new Label("Wartosc 1");&lt;br /&gt;Label l2 = new Label("Nazwa 2");&lt;br /&gt;Label w2 = new Label("Wartosc 2");&lt;br /&gt;&lt;br /&gt;content.setStyleName("table_main");&lt;br /&gt;l1.setStyleName("table_field");&lt;br /&gt;l2.setStyleName("table_field");&lt;br /&gt;w1.setStyleName("table_field");&lt;br /&gt;w2.setStyleName("table_field");&lt;br /&gt;&lt;br /&gt;content.add(l1);&lt;br /&gt;content.add(l2);&lt;br /&gt;content.add(w1);&lt;br /&gt;content.add(w2);&lt;br /&gt;&lt;br /&gt;initWidget(content);&lt;br /&gt;&lt;br /&gt;STYLE:&lt;br /&gt;&lt;br /&gt;.table_main{&lt;br /&gt;width:200px;&lt;br /&gt;display:block;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.table_field{&lt;br /&gt;float:left;&lt;br /&gt;width:100px;&lt;br /&gt;height:20px;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Ponieważ FlowPanel nie wymaga dużo sprawdzeń i inicjalizacji to jego utworzenie jest bardzo proste. Dodanie kolejnych elementów nie wymaga tworzenia struktury drzewa, tak jak w przypadku kontenerów opartych o tablice. Dzięki temu, ilość kodu javascript który musi zostać wykonany jest dużo mniejsza, a co za tym idzie jest mocno zauważalny wzrost wydajności tego rozwiązania w stosunku do standardowej tabeli.&lt;br /&gt;Wada tego rozwiązania jest taka, że jednak nie jest to standardowa tabela, a co za tym idzie, nie ma możliwości skalowania tabeli. Na szczęście w większości dzisiejszych layoutów wielkości są zablokowane na stałe, a więc ten problem dotyczy niewielkiej ilości przypadków.&lt;br /&gt;&lt;br /&gt;PS. Nie mieżyłem wzrostu wydajności, ale wzrost wydajności był zauważalny bez mieżenia. Strona oparta o tabele ładowała się wolno. Strona oparta o to rozwiązanie ładowała się w zasadzie odrazu.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-6677299360023166380?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/6677299360023166380/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2009/05/duza-ilosc-elementow-w-gwt.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/6677299360023166380'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/6677299360023166380'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2009/05/duza-ilosc-elementow-w-gwt.html' title='Duża ilość elementów w GWT'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-1036647974350044521</id><published>2009-04-21T03:35:00.000-07:00</published><updated>2009-04-21T05:17:19.158-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><category scheme='http://www.blogger.com/atom/ns#' term='IceFaces'/><category scheme='http://www.blogger.com/atom/ns#' term='RichFaces'/><title type='text'>Richfaces VS Icefaces</title><content type='html'>Ten artykuł był w sumie jednym z pierwszych które chciałem napisać. Było to spowodowane dylematem który miałem podczas wyboru technologii webowej do następnej aplikacji. W tamtym okresie miałem już doświadczenia z IceFaces i nie byłem do końca przekonany do tej właśnie technologii, natomiast brakowało mi doświadczeń z RichFaces, a był to framework który zapowiadał się bardzo ciekawie. Niestety, nie udało mi się znaleźć na sieci dobrego porównania tych technologii, w związku z tym postanowiłem napisać ten artykuł.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ogółem&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;W ogólnym zarysie obie technologie wyglądają podobnie. Posiadają dokładnie to samo zastosowanie, podobny zakres komponentów oraz oparte są na technologii JSF. Można zauważyć pewne drobne różnice jeżeli idzie o dostępne komponenty, ale posiadają podobną ilość. Osobiście odniosłem jednak wrażenie, że IceFaces ma komponenty jednak troche lepiej dobrane i kompletne, podczas gdy w RichFaces są one odrobinę bardziej surowe, ale tak na prawdę jest to raczej subiektywne odczucie. Na podstawie pobieżnego obejrzenia nie byłem w stanie jednak jednoznacznie wybrać bądź odrzucić żadnej z technologii.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Instalacja&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Instalacja IceFaces nie należała do najprzyjemniejszych. Co prawda mówię o wersji 1.6, a więc mogło się to zmienić, nie mniej taką właśnie wersję instalowałem. O ile praca odbywała się na serwerze WebLogic problemów nie było. Wszystko działało tak jak trzeba. Niestety na etapie instalacji na JBoss (4.2.2) instalacja nawet przykładowej aplikacji nie była możliwa do wykonania. IceFaces zawiera wbudowaną wersję JSF. Jest to również prawda w przypadku serwera JBoss. Jak można się łatwo domyślić wersje te nie były zgodne i nie chciały ze sobą współpracować. Po wielu próbach rozwiązania problemu, włącznie z postami na forum IceFaces z prośbą o pomoc poddałem się. Dwa miesiące później na forum pojawiły się jakieś pomysły rozwiązań, ale jeżeli mam być szczery, to było to obejście problemu (na poziomie serwera) a nie jego rozwiązanie.&lt;br /&gt;&lt;br /&gt;Instalacja RichFaces była stosunkowo prosta. Jedyne co dodatkowo musiałem zrobić, to zainstalować implementacje JSF, ponieważ sam framework był dostarczony bez. Po zainstalowaniu wszystko działało zarówno na serwerze JBoss jak i na WebLogic.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ajax&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Największą zaletą obu frameworków jest obsługa AJAX. Oba podchodzą do problemu trochę inaczej. W przypadku IceFaces Ajax jest wbudowany w komponenty i w zasadzie przezroczysty dla użytkownika. Kontrola nad nim sprowadza sie do kilku ustawień w web.xml oraz parametrach w komponentach. W przypadku RichFaces Ajax jest bardzo widoczny. Jest to rozwiązane za pomocą całej serii dodatkowych komponentów (framework Ajax 4 Java (a4j)). Ten dodatkowy framework jest odpowiedzialny za całą komunikację z serwerem. Niestety oznacza to również, że programista musi napisać dodatkowy kod aby ajax działał w przeciwieństwie do IceFaces, gdzie proces zachodzi automatycznie. Wykorzystanie tego dodatkowego frameworku pozwala jednak na wykorzystanie standardowych komponentów JSF i dodanie do nich obsługi AJAX bez konieczności zastępowania ich. Takie podejście sprawia, że można w łatwy sposób dodać obsługę AJAX do już istniejącej aplikacji. Oba frameworki prezentują również troche inne podejście do odświeżania stanu aplikacji. Oba pozwalają na odpytywanie w celu sprawdzenia czy są nowe dane. Jednak, jeżeli idzie o push, to rozwiązanie zastosowane w obu frameworkach jest nieco inne. W przypadku IceFaces połączenie jest inicjowane ze strony serwera. W przypadku gdy pojawią się jakieś dane, serwer (który utrzymuje cały czas połączenie) wysyła do klienta nowe dane. W przypadku RichFaces klient co jakiś czas odpytuje serwera czy są jakieś dane. Jeżeli serwer przygotował jakąś paczkę danych, to jest ona pobierana. Warto wspomnieć, że RichFaces nie utrzymuje stałego połączenia z serwerem w przeciwieństwie do IceFaces. Osobiście miałem sporo problemów z tym utrzymywanym połączeniem. IceFaces zakładało, że sieć jest idealna, i takie połączenie nie zostanie zerwane. W przypadku zerwania aplikacja nie zachowywała się dobrze. Miało to zostać naprawione w późniejszych wersjach (i jest duża szansa, że obecnie problem już nie występuje).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Problemy&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;W przypadku obu frameworków natrafiłem na kilka problemów.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;IceFaces&lt;/span&gt;&lt;br /&gt;Ponieważ sprawa dotyczy starej wersji frameworku myślę, że nie ma sensu opisywać konkretnych problemów z samym frameworkiem, natomiast postaram się oddać zakres problemów z jakim się spotkałem. Po pierwsze były problemy z konkretnymi komponentami, ale były to raczej niewielkie problemy i były raczej uciążliwe, niż uniemożliwiające prace. Rozwiązanie problemów albo było w repozytorium kodu, albo było planowane do wypuszczenia z następną wersją. Niestety problem na który natrafiłem ja był planowany z następną wersją. Mówię niestety, bo o ile po zmianie wersji problem zniknął, o tyle pojawił się inny dużo poważniejszy w komponencie który wcześniej działał prawidłowo. Sama zmiana wersji (z wersji 1.5.x na 1.6) nie przebiegała w sposób prosty i bezproblemowy. Ponieważ zostało zmienione całkowicie nazewnictwo klas w stylach aplikacja straciła cały wygląd. Wymagało to ręcznej zmiany wszystkich styli na docelowe. W wersji 1.6.1 dodane zostało narzędzie do automatycznej migracji styli, ale niestety było to już dłuższy czas po tym, jak style zostały dostosowane. Może to tylko moje odczucia, ale bardzo mi się nie podobało taki załatwienie sprawy.&lt;br /&gt;Dodanie własnego komponentu pracującego razem z AJAX wymagało sporo wysiłku ze strony programisty. Wszelkie niestandardowe rozwiązania, nie przewidziane przez programistów tworzących Framework były dość trudne do zaimplementowania, ponieważ sam framework stanowi dość zamkniętą całość.&lt;br /&gt;Community jest dość rozbudowane i często chętne do pomocy, ale jednak na odpowiedź trzeba trochę poczekać, szczególnie jeżeli problem jest bardziej złożony.&lt;br /&gt;Dokumentacja do projektu jest dość dobra, jest sporo przykładów.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;RichFaces&lt;/span&gt;&lt;br /&gt;Problemy z samym frameworkiem były podobne do tych z IceFaces. Pojedyńcze elementy nie do końca dobrze funkcjonowały, natomiast znalezienie rozwiązania było znacznie łatwiejsze. Główne problemy natomiast, były nieco innej natury. Do IceFaces można było znaleźć pokaźną ilość przykładów czy HOWTO. Z RichFaces już nie było tak lekko. Ilość przykładów dostarczonych z aplikacją była bardzo skąpa a przykładowy kod demonstrowany w Component Showcase przeklejony żywcem nie zawsze działał. Po analizie kodu źródłowego okazywało się, że źródłowy zawarty na stronie nie posiadał paru elementów koniecznych do działania elementu do którego się odwoływał. Dojście do tego jak dany komponent działał, wymagało przejrzenia kodu źródłowego samej aplikacji demonstracyjnej. Community jest również znacznie mniejsze i trudniej jest uzyskać pomoc w przypadku problemów.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Podsumowanie&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Oba frameworki są oprogramowaniem dobrym i stanowią ciekawą ofertę. RichFaces jest napisany tak, że pozwala na zaimplementowanie bardziej specyficznych rozwiązań dużo niższym kosztem, bo kod jest tak pomyślany, że dodanie obsługi Ajax jest proste a same komponenty w większości są standardową implementacją JSF (Sun lub MyFaces). Niestety, to samo rozwiązanie powoduje, że ta dodatkowa kontrola wymaga pisania dodatkowego kodu, a więc dla aplikacji która nie stosuje niestandardowych rozwiązań i liczy się czas, IceFaces jest dużo lepszym rozwiązaniem. Dodatkowo IceFaces jest znacznie lepszy jeżeli idzie o poznanie samego frameworku. Dokumentacja, dobre i duże community sprawia ze ten framework zdobywa coraz większe uznanie. Ja osobiście jednak bardziej polubiłem RichFaces. Miałem z nim znacznie mniej problemów a jednocześnie daje mi dużo większą kontrolę nad tym co się dzieje w aplikacji, co mi osobiście bardzo odpowiada. W przypadku IceFaces czas który został zaoszczędzony na dodatkowym kodzie, został wykorzystany na pisanie łatek do niedziałającego kodu samego frameworku, lub poprawianie nowych problemów po wgraniu kolejnej wersji poprawiającej inne błędy.&lt;br /&gt;&lt;br /&gt;Jeżeli miał bym wybierać framework oparty o JSF do następnego projektu to skłaniał bym się bardziej w kierunku RichFaces, jako bardziej dopracowanego i dającego większą swobodę.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-1036647974350044521?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/1036647974350044521/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2009/04/richfaces-vs-icefaces.html#comment-form' title='Komentarze (1)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/1036647974350044521'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/1036647974350044521'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2009/04/richfaces-vs-icefaces.html' title='Richfaces VS Icefaces'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-674699057843093316</id><published>2009-04-15T02:06:00.000-07:00</published><updated>2009-04-15T03:07:43.840-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GWT'/><category scheme='http://www.blogger.com/atom/ns#' term='SSB'/><title type='text'>GWT oraz Session Beans</title><content type='html'>&lt;i&gt;Uwaga do tego artykułu.&lt;/i&gt;&lt;br /&gt;&lt;code&gt;Kod zawarty w tym wpisie w części nie jest kodem napisanym przezemnie. Sam pomysł został zaczerpnięty z grup googlowych, ja postanowiłem, że pomysł jest na tyle ciekawy, że warto go przedstawić.&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Standardowo GWT nie jest zawarte w transakcji. Ten stan raczej nie jest do końca wygodny, szczególnie jeżeli odbywa się jakieś przetwarzanie po stronie serwera. Standardowa struktura aplikacji jest następująca&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Klient (przeglądarka)&lt;/li&gt;&lt;li&gt;Serwer (GWT)&lt;/li&gt;&lt;li&gt;Serwer (EJB) (logika biznesowa)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;DB&lt;/li&gt;&lt;/ul&gt;Oczywiście ta struktura może być różna, w zależności od aplikacji, ale generalnie wygląda podobnie. Problem polega na tym, że na poziomie &lt;code&gt;Server(GWT)&lt;/code&gt; dane nie są objęte transakcjami. Okazuje się jednak, że przy pomocy stosunkowo prostego zabiegu da się tą warstwę całkowicie pominąć.&lt;br /&gt;&lt;br /&gt;Standardowo aplikacja GWT wymaga zdefiniowania 2 interfejsów oraz ich implementacji. Interfejsy są zrobione standardowo&lt;br /&gt;&lt;br /&gt;Service:&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;public interface SmapleService extends RemoteService {&lt;br /&gt;  // Sample interface method of remote interface&lt;br /&gt;  String getMessage(String msg);&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;   * Utility/Convenience class.&lt;br /&gt;   * Use SmapleService.App.getInstance () to access static instance of SmapleServiceAsync&lt;br /&gt;   */&lt;br /&gt;  public static class App {&lt;br /&gt;      private static SmapleServiceAsync app = null;&lt;br /&gt;&lt;br /&gt;      public static synchronized SmapleServiceAsync getInstance() {&lt;br /&gt;          if (app == null) {&lt;br /&gt;              app = (SmapleServiceAsync) GWT.create(SmapleService.class);&lt;br /&gt;              ((ServiceDefTarget) app).setServiceEntryPoint(GWT.getModuleBaseURL() +&lt;br /&gt;                                       "smaple.Smaple/SmapleService");&lt;br /&gt;          }&lt;br /&gt;          return app;&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;ServiceAsync:&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;public interface SmapleServiceAsync {&lt;br /&gt;  void getMessage(String msg, AsyncCallback&lt;string&gt; async);&lt;br /&gt;}&lt;br /&gt;&lt;/string&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Standardowa implementacja wygląda tak:&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;pre&gt;&lt;code&gt; &lt;br /&gt;public class SmapleServiceImpl &lt;br /&gt;       extends RemoteServiceServlet &lt;br /&gt;       implements SmapleService {&lt;br /&gt;  // Implementation of sample interface method&lt;br /&gt;  public String getMessage(String msg) {&lt;br /&gt;      return "Client said: \"" + msg + "\"&lt;br /&gt;Server answered: \"Hi!\"";&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Ten kod niestety nie pozwala na transakcyjność. Wystarczy jednak zrobić kilka niewielkich zmian. Po pierwsze klasa &lt;code&gt;SampleServiceImpl&lt;/code&gt; nie powinna implementować interfejsu &lt;code&gt;SampleService&lt;/code&gt;. Po drugie, należy przeciążyć metodę processCall odpowiedzialną za obsługę przychodzących wywołąń tak, aby wywoływała metody z SSB.&lt;br /&gt;&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;public class SampleServiceImpl &lt;br /&gt;       extends RemoteServiceServlet {&lt;br /&gt;  private static final Logger log = Logger.getLogger(SampleServiceImpl.class);&lt;br /&gt;&lt;br /&gt;  public String processCall(String payload) throws SerializationException {&lt;br /&gt;      try {&lt;br /&gt;          Object bean = getBean();&lt;br /&gt;          synchronized (bean) {&lt;br /&gt;              RPCRequest rpcRequest = RPC.decodeRequest(payload, bean.getClass());&lt;br /&gt;              log.debug("RPC METHOD CALL: " + &lt;br /&gt;                        rpcRequest.getMethod() + &lt;br /&gt;                        " PARAMETERS: " + &lt;br /&gt;                        Arrays.toString(rpcRequest.getParameters()));&lt;br /&gt;              return RPC.invokeAndEncodeResponse(bean, rpcRequest.getMethod(),&lt;br /&gt;                      rpcRequest.getParameters());&lt;br /&gt;          }&lt;br /&gt;&lt;br /&gt;      } catch (IncompatibleRemoteServiceException ex) {&lt;br /&gt;          ex.printStackTrace();&lt;br /&gt;          return RPC.encodeResponseForFailure(null, ex);&lt;br /&gt;      } catch (NamingException ne) {&lt;br /&gt;          ne.printStackTrace();&lt;br /&gt;          return RPC.encodeResponseForFailure(null, ne);&lt;br /&gt;      }&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public ISampleBeanService getBean() throws NamingException {&lt;br /&gt;      final HttpSession httpSession = getThreadLocalRequest().getSession();&lt;br /&gt;      ISampleService bean = (ISampleBeanService) httpSession.getAttribute("SampleBeanService");&lt;br /&gt;&lt;br /&gt;      if (bean != null) try {&lt;br /&gt;          // Call a method to test whether the bean is still live; &lt;br /&gt;          // if it's dead, we'll re-create it&lt;br /&gt;          synchronized (bean) {&lt;br /&gt;              bean.isAlive();&lt;br /&gt;              log.debug("Bean alive");&lt;br /&gt;          }&lt;br /&gt;      } catch (NoSuchEJBException ex) {&lt;br /&gt;          log.debug("Bean dead");&lt;br /&gt;          bean = null;&lt;br /&gt;          httpSession.removeAttribute("SampleBeanService");&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      if (bean == null) {&lt;br /&gt;          InitialContext ic = new InitialContext();&lt;br /&gt;          Object objRef = ic.lookup("sample/SampleBeanService/local");&lt;br /&gt;          bean = (ISampleBeanService) PortableRemoteObject.narrow(objRef,&lt;br /&gt;                                      ISampleBeanService.class);&lt;br /&gt;          httpSession.setAttribute("SampleBeanService", bean);&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      return bean;&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Warto zwrocic uwage, ze nie zgadzaja sie nazwy interfejsow. Bean korzysta z interfejsu &lt;code&gt;ISampleBeanService&lt;/code&gt; podczas gdy GWT korzysta z &lt;code&gt;SampleService&lt;/code&gt;. To nie jest błąd.&lt;br /&gt;&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;public interface ISampleBeanService extends SampleService {&lt;br /&gt;  public boolean isAlive();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Ten interfejs jest pomocniczy implementuje tylko metodę isAlive(), wykorzystywaną do sprawdzenia czy SSB zostało stworzone, oraz czy jest nadal dostępne.&lt;br /&gt;&lt;br /&gt;Aby całość działała brakuje już tylko samego SSB:&lt;br /&gt;&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;@Stateless(name = "SampleBeanService")&lt;br /&gt;@Local(ISampleBeanService.class)&lt;br /&gt;public class SampleService implements ISampleBeanService {&lt;br /&gt;  // Implementation of sample interface method&lt;br /&gt;  public String getMessage(String msg) {&lt;br /&gt;      return "Client said: \"" + msg + "\"&lt;br /&gt;Server answered: \"Hi!\"";&lt;br /&gt;  }&lt;br /&gt;  public boolean isAlive() {&lt;br /&gt;      return true;&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Bean jak widać jest standardowym beanem. Nie wymaga niczego nadzwyczajnego.&lt;br /&gt;&lt;br /&gt;Napotkałem na dwa problemy przy implementacji tego rozwiązania.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Problem z sesją. Na dobrą sprawę, jeżeli użyjemy tego rozwiązania pojawia się problem z przechowywaniem danych. Standardowo w GWT dane wrzucane są do sesji HTTP. Niestety w przypadku Statless bean z założenia dane nie są w nim trzymane, a z poziomu SSB nie ma dostępu do Sesji HTTP. Rozwiązaniem jakie się w sumie samo nasuwa są Statefull Session Bean, które w zasadzie właśnie do tego celu były przewidziane.&lt;/li&gt;&lt;li&gt;Problem z kompilacją modułów (w Idei). Chodzi o zależności między modułami. W tym przypadku SSB musi być w module WEB, a to nie jest dobre rozwiązanie. Na szczęście znalazłem inne rozwiązanie, które zostało opisane w osobnym wpisie w tym blogu.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-674699057843093316?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/674699057843093316/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2009/04/gwt-oraz-session-beans.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/674699057843093316'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/674699057843093316'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2009/04/gwt-oraz-session-beans.html' title='GWT oraz Session Beans'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-5801187299710920022</id><published>2009-03-25T08:54:00.000-07:00</published><updated>2009-04-15T02:58:16.092-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GWT'/><title type='text'>Wykorzystanie obiektów z warstw niższych w warstwie WEB GWT.</title><content type='html'>Jednym z problemów jaki miałem w korzystaniu z GWT było wykorzzystanie tych samych obiektów na poziomie warstwy logiki biznesowej, czy DAO oraz na poziomie warstwy WEB. Teoretycznie są to obiekty POJO, więc problemów być nie powinno, jednak wymaganie, z drugiej strony ich deklaracje znajdują się w module web (a więc w większości przypadków w plikach war).&lt;br /&gt;Okazuje się jednak, że tak być nie musi. Klasy muszą się znajdować w katalogu GWT (w ktatlogu &lt;code&gt;client&lt;/code&gt;) w czasie kompilacji kodu GWT, ale później można je przenieść w dowolne miejsce w aplikacji.&lt;br /&gt;&lt;br /&gt;Muszą zostać jednak spełnione dwa warunki:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Pakiet w którym znajduje się klasa musi być nadal ten sam.&lt;/li&gt;&lt;li&gt;Moduł web musi mieć dostęp do skompilowanych źródeł.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;W związku z tym struktura, w której całość działała w mojej aplikacji wyglądała tak:&lt;br /&gt;&lt;br /&gt;EAR&lt;br /&gt;&lt;ul&gt;&lt;li&gt;dao.jar&lt;/li&gt;&lt;li&gt;business.jar&lt;/li&gt;&lt;li&gt;web.war&lt;/li&gt;&lt;li&gt;META-INF&lt;/li&gt;&lt;/ul&gt;WAR&lt;br /&gt;&lt;ul&gt;&lt;li&gt;images&lt;/li&gt;&lt;li&gt;css&lt;/li&gt;&lt;li&gt;WEB-INF&lt;/li&gt;&lt;li&gt;WEB-INF -&gt; lib -&gt; web.jar&lt;br /&gt;&lt;/li&gt;&lt;li&gt;META-INF&lt;/li&gt;&lt;li&gt;[...].js&lt;/li&gt;&lt;li&gt;[...].html&lt;/li&gt;&lt;/ul&gt;Co istotne, obiekty zawarte były w dao.jar a nie w web.jar.&lt;br /&gt;&lt;br /&gt;Oczywiście tego typu struktura wymaga aby biblioteka GWT była importowana w module DAO (ze względu na interfejs &lt;code&gt;IsSerializable&lt;/code&gt;), ale jest to niewielkie poświęcenie za możliwość korzystania z tych samych obiektów w module GWT oraz w modulach biznesowych. Dzięki takiemu rozwiązaniu mogę załadować obiekt, na przykład przy pomocy JPA z bazy a następnie przekazać go bezpośrednio do warstwy webowej.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Kompilacja&lt;/i&gt;&lt;br /&gt;Całość została przezemnie zautomatyzowana przy pomocy skryptów antowych.&lt;br /&gt;Standardowo obiekty są umieszczone w module DAO.&lt;br /&gt;Przed kompilacją Obiekty są przenszone do modułu GWT.&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;       &amp;lt;move todir="dao/src/pl/project/client/objects"&amp;gt;&lt;br /&gt;           &amp;lt;fileset dir="webui/src/pl/project/client/objects"/&amp;gt;&lt;br /&gt;       &amp;lt;/move&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Ant, kompiluje moduł GWT przy pomocy taska antowego dostępnego w bibliotece ant-gwt.&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;       &amp;lt;taskdef resource="dk/contix/ant/gwt/ant-gwt.xml" &lt;br /&gt;                classpathref="project.classpath"/&amp;gt;&lt;br /&gt;       &amp;lt;gwtcompile destdir="${build.dir}/gwt-web" optimize="true"&amp;gt;&lt;br /&gt;           &amp;lt;fileset dir="${module.web.basedir}/web/src"&amp;gt;&lt;br /&gt;               &amp;lt;include name="**/*.gwt.xml"/&amp;gt;&lt;br /&gt;           &amp;lt;/fileset&amp;gt;&lt;br /&gt;       &amp;lt;/gwtcompile&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;Przenosze spowrotem obiekty do DAO&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;       &amp;lt;move todir="web/src/pl/project/client/objects"&amp;gt;&lt;br /&gt;           &amp;lt;fileset dir="dao/src/pl/project/client/objects"/&amp;gt;&lt;br /&gt;       &amp;lt;/move&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Kompiluje pozostałe elementy a następnie składam tworze pliki JAR, WAR oraz EAR&lt;br /&gt;&lt;br /&gt;Warto wspomnieć, że GWT nie miało problemów z anotacjami.&lt;br /&gt;&lt;br /&gt;Następnym razem napisze jak wymusić na GWT aby wywoływało bezpośrednio Beana EJB (na przyklad Stateless Session Bean), dzięki czemu wszystkie operacje wykonywane są wewnątrz transakcji.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-5801187299710920022?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/5801187299710920022/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2009/03/wykorzystanie-obiektow-z-warstw.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/5801187299710920022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/5801187299710920022'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2009/03/wykorzystanie-obiektow-z-warstw.html' title='Wykorzystanie obiektów z warstw niższych w warstwie WEB GWT.'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-4070833014885233804</id><published>2009-03-23T13:36:00.000-07:00</published><updated>2009-04-15T02:59:09.257-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GWT'/><category scheme='http://www.blogger.com/atom/ns#' term='Exception'/><title type='text'>Obsługa wyjątków w GWT</title><content type='html'>Sprawa jest stosunkowo prosta od momentu gdy powstało GWT 1.5. Mechanizm RPC zastosowany w GWT pozwala na przekazywanie wyjątków powstałych w warstwach niższych, jednak te wyjątki muszą spełniać pewne warunki.&lt;br /&gt;&lt;br /&gt;Wyjątek musi dziedziczyć po &lt;code&gt;Throwable&lt;/code&gt; (a więc na przykład &lt;code&gt;Exception&lt;/code&gt;). Musi implementować &lt;code&gt;java.io.Serializable&lt;/code&gt; oraz co najważniejsze musi implementować &lt;code&gt;com.google.gwt.user.client.rpc.IsSerializable&lt;/code&gt;. Ta ostatnia klasa jest wymagana dla przekazywania wszelkich obiektów poprzez RPC, a więc dotyczy to również wyjątków.&lt;br /&gt;&lt;br /&gt;Wyjątek jest przechwytywany w metodzie &lt;code&gt;public void onFailure(Throwable caught)&lt;/code&gt; i zawarty jest w zmiennej caught. Dalszą obsługę wyjątku możemy robić już w standardowy sposób.&lt;br /&gt;&lt;br /&gt;Przykładowy kod:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Klasa błędu&lt;/i&gt;&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;import com.google.gwt.user.client.rpc.IsSerializable;&lt;br /&gt;import java.io.Serializable;&lt;br /&gt;&lt;br /&gt;public class MyException extends Exception&lt;br /&gt;                       implements Serializable, IsSerializable {&lt;br /&gt;String name = getClass().getName();&lt;br /&gt;&lt;br /&gt;public String getName() {&lt;br /&gt;return name;&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Obsługa błedu&lt;/i&gt;&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;MyAppService.App.getInstance().method(new AsyncCallback&amp;lt;Void&amp;gt;() {&lt;br /&gt;public void onFailure(Throwable caught) {&lt;br /&gt;  if (caught instanceof MyException)&lt;br /&gt;    handleException((MyException)caught);&lt;br /&gt;  else&lt;br /&gt;    handleUnknownException(caught);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void onSuccess(Void result) {&lt;br /&gt;  [...]&lt;br /&gt;}&lt;br /&gt;});&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Deklaracja metody w MyAppAsync&lt;/i&gt;&lt;br /&gt;&lt;div style="overflow-x:auto"&gt;&lt;code&gt;&lt;pre&gt;&lt;br /&gt;public interface MyAppAsync(){&lt;br /&gt;void method(AsyncCallback&amp;lt;Void&amp;gt; async);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Niestety to rozwiązanie posiada dwa poważne problemy.&lt;br /&gt;Po pierwsze pozwala na przesłanie tylko własnych klas błędów.&lt;br /&gt;Po drugie wymaga aby klasa błędu była w pakiecie &lt;code&gt;client&lt;/code&gt; w module zawierającym kod GWT. Oznacza to poważny problem w zależnościach między modułami.&lt;br /&gt;&lt;br /&gt;Rozwiązanie drugiego problemu w skrócie:&lt;br /&gt;Jest możliwa kompilacja modułu GWT przy pomocy kompilatora GWT (do postaci javascript), a następnie kompilacja źródeł z innym modułem. To może być na przykład moduł trzymający wszystkie obiekty. Istotne jest tylko, aby moduł GWT posiadał dostęp do tej skompilowanej klasy oraz klasa znajdowała się w odpowiednim pakiecie.&lt;br /&gt;&lt;br /&gt;Ten konkretny problem postaram się opisać szerzej następnym razem.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-4070833014885233804?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/4070833014885233804/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2009/03/obsuga-wyjatkow-w-gwt.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/4070833014885233804'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/4070833014885233804'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2009/03/obsuga-wyjatkow-w-gwt.html' title='Obsługa wyjątków w GWT'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1707592354142736969.post-4781014952036736883</id><published>2009-03-23T13:14:00.000-07:00</published><updated>2009-03-23T13:26:16.134-07:00</updated><title type='text'>Wstęp</title><content type='html'>Postanowiłem stworzyć tego bloga, aby podzielić się z szerszym gronem moimi frustracjami oraz spostrzeżeniami. Może ktoś, dzięki moim spostrzeżeniom w przyszłości uniknie błędów, z którymi ja się spotkałem.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1707592354142736969-4781014952036736883?l=exverxes.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://exverxes.blogspot.com/feeds/4781014952036736883/comments/default' title='Komentarze do posta'/><link rel='replies' type='text/html' href='http://exverxes.blogspot.com/2009/03/wstep.html#comment-form' title='Komentarze (0)'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/4781014952036736883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1707592354142736969/posts/default/4781014952036736883'/><link rel='alternate' type='text/html' href='http://exverxes.blogspot.com/2009/03/wstep.html' title='Wstęp'/><author><name>Exverxes</name><uri>http://www.blogger.com/profile/12106291399383336193</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
