DWR = Direct Web Remoting = ?
Słowem wstępu
Mroczna historia na dobry początek
Przed monitorem jednego z terminali Biblioteki Uniwersyteckiej spędzałem kolejną dłużącą się bez końca godzinę. Na zewnątrz niebo nabierało coraz ciemniejszych barw. Poszukiwałem ciekawego tematu na nowy artykuł na j2ee.pl.
Wielogodzinna praca powodowała, że zaczynałem odczuwać nadchodzące zmęczenie. Postanowiłem podarować sobie chwilę odpoczynku od zagadnień IT. Nierozsądnie wpisałem w pasku przeglądarki tvn24.pl – strona chwilę się wczytywała. Wziąłem ostatni już łyk Red Bulla. Jak przez mgłę spoglądałem na treści atakujące moją bezbronną świadomość. W prawym dolnym rogu ujrzałem umieszczoną sondę. Zupełnie bez powodu ogarnęła mnie nagła, nieodparta ochota na to, żeby zagłosować… Lecz nagle poczułem na skórze powiew mroźnego powietrza, który w parnej sali Biblioteki wydawał się pojawić jakby znikąd. To co działo się następnie nie sposób opisać jakimikolwiek słowami. Przez chwilę zdawało mi się, że tuż za moimi plecami bezgłośnie niczym rycerz ciemności przemknął Premier Jarosław Kaczyński cenzurując swym wzrokiem oglądane przeze mnie treści. Modliłem się, żeby to wszystko nie było prawdą – nie chciałem, żeby Premier zobaczył oglądania jakich pornograficznych treści dopuszczają się studenci. Wciąż oszołomiony tym, co wydarzyło się przed chwilą postanowiłem zaczerpnąć świeżego powietrza.
(Aby przejść do części praktycznej kliknij tutaj.)
DWR?
Po krótkiej przerwie powróciłem pełen energii do dalszej pracy. Początkowo miałem kilka pomysłów, aby w końcu zdecydować się na przedstawienie frameworka Direct Web Remoting, czyli jak twierdzą jego autorzy łatwego sposobu na Ajax dla programistów Javy. Przyznam się, że dałem się im przekonać. W jakim jednak celu tworzyć kolejne wprowadzenie do tak dobrze znanego i jeszcze lepiej udokumentowanego szkieletu programistycznego. To zupełnie tak jakby dzisiaj pisać kolejną książkę o podstawowych zagadnieniach Springa, czy Hibernate‘a. Jest na sali dzisiaj ktokolwiek, kto by nie miał do czynienia z bardzo dobrymi publikacjami opisującymi te zagadnienia?… Tak właśnie myślałem!
A może jednak?
Mimo tych szargających mną wątpliwości postanowiłem dokonać krótkiej prezentacji frameworka DWR, gdyż zachowując wszystkie podobieństwa do wyżej wymienionych technologii – z DWR wydaje się być jednak nieco inaczej. Ja osobiście bliżej przyjrzeć się temu frameworkowi miałem dopiero okazję stosunkowo niedawno. Z badań opinii publicznej ;), jakie pozwoliłem sobie przeprowadzić okazało się, że nie jestem jedyną osobą, która do niedawana o DWR niewiele wiedziała – a nawet jeżeli wiedziała, to była to wiedza czysto teoretyczna.
DWR + Spring
Aktualną stabilną gałęzią jest branch 2.0.x, ale trwają intensywne prace nad wersją 3.0, która już niebawem powinna ujrzeć światło dzienne. W tym artykule będę korzystał z ostatniej dostępnej wersji stabilnej, czyli na dzień dzisiejszy – 2.0.3. Swoją uwagę skupię przede wszystkim na możliwościach integracji tego frameworka ze Springiem. Po tym nieco teoretycznym wstępie przedstawię działający przykład aplikacji webowej zwracając uwagę na szczególnie interesujące i pomocne elementy z punktu widzenia developera. Przedstawione rozwiązanie będzie miało charakter Proof of Concept, stąd też przykład którym się posłużę będzie wyjątkowo trywialny.
Cel
Gdy inwestuje się w coś swój (czy też czyjś) czas warto wiedzieć, co właściwie ma być finalnym efektem włożonej pracy. Moim celem będzie pokazanie w jaki sposób umożliwić bezpośrednie wywoływania z poziomu JavaScript metod serwisów zdefiniowanych (jako beany) w kontekście aplikacyjnym Springa.
Narzędzia, frameworki i biblioteki wykorzystane do wykonania tego ćwiczenia
- Narzędzia
- Eclipse/JDT
- maven2 – posłuży nam do budowania, deployowania oraz testowania aplikacji
- maven-jetty-plugin – wtyczka do mavena pozwalająca na uruchamianie serwera aplikacji, jakim jest Jetty z poziomu mavena (bardzo przyjemne oraz efektywne narzędzie)
- Framworki i biblioteki
- Spring Framework
- DWR
- jQuery
Część praktyczna
(Aby pobrać pełne źródła tworzonej aplikacji kliknij tutaj.)
Przygotowanie standardowego szablonu aplikacji webowej
Rozpocznę od utworzenia pliku pom.xml z myślą o aplikacji sieciowej korzystającej ze Spring Web MVC. Z pewnością robiłeś już takie rzeczy wiele razy.
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>pl.jcommerce.example</groupId>
<artifactId>dwr-example</artifactId>
<version>0.1</version>
<packaging>war</packaging>
<name>j2ee.pl DWR example</name>
<build>
<defaultGoal>package</defaultGoal>
<finalName>dwr-example</finalName>
<resources>
<resource>
<directory>src/main/webapp/WEB-INF</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<encoding>utf8</encoding>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<configuration>
<connectors>
<connector implementation="org.mortbay.jetty.nio.SelectChannelConnector">
<port>8080</port>
<maxIdleTime>60000</maxIdleTime>
</connector>
</connectors>
<scanIntervalSeconds>5</scanIntervalSeconds>
<webAppConfig>
<contextPath>/dwr-example</contextPath>
</webAppConfig>
</configuration>
</plugin>
</plugins>
</build>
<pluginRepositories>
<pluginRepository>
<id>mortbay-repo</id>
<name>mortbay-repo</name>
<url>http://jetty.mortbay.org/maven2/release</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>2.5.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>2.5.2</version>
</dependency>
</dependencies>
</project>
Element, który może być dla Ciebie nowy i być może powinieneś na niego zwrócić uwagę, to konfiguracja wtyczki Jetty. Jeżeli nie znałeś tego narzędzia do tej pory, to z czystym sumieniem mogę Ci je polecić.
Teraz utworzę standardową dla mavena strukturę katalogów i dodam kolejne ważne z punktu widzenia projektu pliki.
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>j2ee.pl DWR example</display-name>
<description></description>
<listener>
<display-name>Spring context-loader</display-name>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>
org.springframework.web.filter.CharacterEncodingFilter
</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Następnie wykonam polecenie:
mvn eclipse:eclipse -DdownloadSources=true
Po pomyślnym wykonaniu tego polecenia zostaną ściągnięte wszystkie wymagane pakiety wraz ze źródłami oraz zostaną utworzone pliki projektu Eclipse, co umożliwi nam pracę z poziomu IDE. Istnieje wiele innych sposobów integracji Eclipse z projektem maven2, ale żaden z nich nie działa do końca perfekcyjnie. W tym jednak wypadku wtyczka eclipse do mavena zdaje egzamin.
Teraz już z poziomu IDE uzupełnie konfigurację dodając pliki applicationContext.xml oraz dispatcher-servlet.xml nie zawierające na razie jednak żadnych definicji beanów.
Pierwsze uruchomienie aplikacji
Jeżeli wszystko poszło zgodnie z planem, to po wykonaniu polecenia
mvn jetty:run
powinnienem być w stanie wywołać z przeglądarki adres http://localhost:8080/dwr-example/.
Warto zauważyć, że począwszy od tej chwili po każdej zmianie dokonanej przeze mnie w plikach projektu Jetty automatycznie dokona ponownego deploymentu aplikacji. Jeżeli zmiana plików projektu nie wywoła takiego działania najprawdopodobniej należy dodać dodatkowe wpisy do elementu konfiguracyjnego wtyczki Jetty o nazwie scanTargets.
Aplikacja owszem działa, ale trudno na razie być zachwyconym jej funkcjonalnością.
Dodanie serwisu do aplikacji
Dodam teraz 3 pliki java, które będą definiowały serwis, który będę chciał w przyszłości wołać z poziomu JavaScriptu. Posłużę się prostym serwisem, który będzie posiadał zaledwie jedną metodę. Do określonego przeze mnie celu będzie to zupełnie wystarczające.
package pl.jcommerce.example.dwr.service;
import pl.jcommerce.example.dwr.model.Product;
/**
* @author Michal Mally
*/
public interface ProductService {
public Product getProductByCode(String code);
}
package pl.jcommerce.example.dwr.service;
import java.math.BigDecimal;
import java.util.HashSet;
import java.util.Random;
import java.util.Set;
import pl.jcommerce.example.dwr.model.Product;
/**
* @author Michal Mally
*/
public class ProductServiceDummyImpl implements
ProductService {
private static final Set<Product> products;
private static final Random random = new Random();
static {
products = new HashSet<Product>();
products.add(new Product("CSS",
"CSS: The Missing Manual", "O'Reilly Media", 3,
BigDecimal.valueOf(2309, 2)));
products.add(new Product("JQR", "jQuery in Action",
"Manning Publications", 4, BigDecimal.valueOf(2639,
2)));
products.add(new Product("DWR",
"Practical DWR 2 Projects", "Apress", 0, BigDecimal
.valueOf(3101, 2)));
}
@Override
public Product getProductByCode(String code) {
for (final Product product : products) {
if (product.getCode().equals(code.toUpperCase())) {
return product;
}
}
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
package pl.jcommerce.example.dwr.model;
import java.math.BigDecimal;
/**
* @author Michal Mally
*/
public class Product {
private String code;
private String name;
private String producer;
private int itemsAvailable;
private BigDecimal price;
public Product() {
// intentionally left blank
}
public Product(String code, String name, String producer,
int itemsAvailable, BigDecimal price) {
super();
this.code = code;
this.name = name;
this.producer = producer;
this.itemsAvailable = itemsAvailable;
this.price = price;
}
// (...) getters/setters
}
Ponieważ całość będę uruchamiał lokalnie w implementacji serwisu celowo zostało dodane losowe opóźnienie, aby móc symulować realne warunki, jakie mają miejsce w Internecie. Mianowicie często zdarza się sytuacja, że odpowiedzi na wysyłane żądanie nie przychodzą w tej samej kolejności w jakiej zostały wysłane żądania.
Dodam teraz odpowiedni wpis do pliku applicationContext.xml.
<bean id="productService" class="pl.jcommerce.example.dwr.service.ProductServiceDummyImpl"> </bean>
A gdzie ten DWR?
Będąc w tym miejscu dodanie wsparcia dla DWR zajmie mi już zaledwie kilka minut.
Do pliku web.xml dodaje definicję servletu DWR oraz odpowiednie do niego mapowanie.
<servlet>
<servlet-name>dwr</servlet-name>
<servlet-class>org.directwebremoting.spring.DwrSpringServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dwr</servlet-name>
<url-pattern>/dwr/*</url-pattern>
</servlet-mapping>
Servlet DWR skonfigurowałem w taki sposób, aby mieć możliwość debugowania.
Teraz ponownie modyfikuje plik applicationContext.xml doprowadzając go do finalnej postaci.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.directwebremoting.org/schema/spring-dwr
http://www.directwebremoting.org/schema/spring-dwr-2.0.xsd">
<dwr:configuration>
<dwr:convert type="bean" class="pl.jcommerce.example.dwr.model.Product"/>
</dwr:configuration>
<bean id="productService" class="pl.jcommerce.example.dwr.service.ProductServiceDummyImpl">
<dwr:remote javascript="ProductService">
<dwr:include method="getProductByCode"/>
</dwr:remote>
</bean>
</beans>
Do zdefiniowanego już wcześniej beana productService dodaję tag dwr:remote określając, że bean, którego to dotyczy ma być dostępny z poziomu JavaScriptu pod nazwą ProductService oraz, że interesuje mnie wywoływanie metody getProductByCode().
Nie można także zapomnieć o tagu dwr:configuration, który jest wymagany niezależnie od tego, czy zawiera jakieś dodatkowe elementy. W moim przypadku określam, że klasa Product będzie transformowana z/do obiektu JavaScript przy użyciu dostępnych w niej getterów/setterów.
Pamiętać należy również o dodaniu nowej zależności do pliku pom.xml. Po tej operacji należy także ponownie wywołać mvn eclipse:eclipse -DdownloadSources=true.
<dependency>
<groupId>org.directwebremoting</groupId>
<artifactId>dwr</artifactId>
<version>2.0.2</version>
</dependency>
Testowanie DWR przy pomocy dostarczonych wraz z frameworkiem narzędzi
Przyszedł czas na sprawdzenie, czy wszystko się powiodło. Wpisuję w przeglądarce adres http://localhost:8080/dwr-example/dwr/. Moim oczom ukazuje się lista znanych klas dla DWR:
ProductService (pl.jcommerce.example.dwr.service.ProductServiceDummyImpl)
Klikam na ProductService i uzyskuję dzięki temu dostęp do bardzo przyjemnego narzędzie pozwalającego na przetestowanie wywołania serwisu. Odnajduję metodę getProductByCode(), wpisuje jako argument jej wywołania “CSS” i klikam Execute.
W wyskakującym okienku (alert()) uzyskuje dokładne informacje o produkcie o kodzie CSS. Zatem wywołanie się powiodło :). Warto zauważyć, że także na konsoli Jetty widać wpis o wykonanym zapytaniu.
Tworzenie strony HTML korzystającej z DWR
Ostatnim już elementem tego tutoriala będzie utworzenie strony HTML umożliwiającej wyszukiwanie produktów po ich kodach bez konieczności przeładowania strony. Przykład trywialny, ale w tym wypadku wystarczający.
Ponieważ zamierzam korzystać z JavaScriptu wspomogę się biblioteką jQuery, która znacznie to ułatwia. Bibliotekę umieszczam w folderze /src/main/webapp/script/js/. Polecam bliższe zapoznanie się z tą biblioteką JS.
A teraz już źródło samej strony JSP oraz krótki opis tego, co się wewnątrz jej źródła dzieje.
<%@ page pageEncoding="UTF-8" %><?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Product search</title>
<script type="text/javascript" src="${pageContext.request.contextPath}/script/js/jquery-1.2.3.min.js"></script>
<script type='text/javascript' src='${pageContext.request.contextPath}/dwr/interface/ProductService.js'></script>
<script type='text/javascript' src='${pageContext.request.contextPath}/dwr/engine.js'></script>
<script type="text/javascript">
// <![CDATA[
var lastRequestId = 0;
function onProductReceived(product) {
if (product != null) {
$("#productDescription").show();
$("#invalidCode").hide();
$("#productCode").html(product.code);
$("#productName").html(product.name);
$("#productProducer").html(product.producer);
$("#productItemsAvailable").html(product.itemsAvailable);
$("#productPrice").html(product.price);
return;
}
$("#invalidCode").show();
$("#productDescription").hide();
}
$(document).ready(
function(){
$("#productDescription").hide();
$("#invalidCode").hide();
$("#productCodeInput").keyup(
function() {
var requestId = ++lastRequestId;
ProductService.getProductByCode($(this).val(), {
callback: function(product) {
if (requestId != lastRequestId) {
return;
}
onProductReceived(product);
}
}
)
}
);
}
);
// ]]>
</script>
</head>
<body>
<p>Enter product code <input id="productCodeInput" type="text"/></p>
<div id="productDescription">
<p>Product code: <span id="productCode"></span></p>
<p>Product name: <span id="productName"></span></p>
<p>Producer: <span id="productProducer"></span></p>
<p>Items available: <span id="productItemsAvailable"></span></p>
<p>Price: <span id="productPrice"></span></p>
</div>
<div id="invalidCode">
<p><strong>You have chosen invalid code! Please choose another one.</strong></p>
</div>
</body>
</html>
W bloku head załączam źródło biblioteki jQuery oraz kod wygenerowany przez DWR będący naszym punktem dostępu do serwisu ProductService. Trochę niżej definiujemy funkcję onProductChange, która będzie wywoływana w momencie uzyskania odpowiedzi z serwisu. Jej zadaniem będzie podmiana części informacji na stronie zgodnie z aktualnie wprowadzonym przez użytkownika kodem produktu.
Zapytanie do serwisu wysyłam każdorazowo po tym jak zajdzie zdarzenie zwolnienia przez użytkownika klawisza. W prawdziwych aplikacjach całkowicie odradzam tą technikę, jednak dla celów tego artykułu jest ona wystarczająca.
Ponieważ użytkownik może z dosyć dużą częstotliwością powodować wysłanie żądań do serwisu musimy się zabezpieczyć przed sytuacją w której nieaktualna odpowiedź nadpisze aktualną wartość. Do tego posłuży nam zmienna requestId. Przy każdorazowym odebraniu odpowiedzi z serwisu będziemy sprawdzali, czy nie zostało przypadkiem wysłane nowsze żądanie. W takim przypadku starsza odpowiedź zostanie przeze mnie zignorowana.
Gdybym nie potrzebował stosować wyżej opisanej techniki wywołanie serwisu mogłoby wyglądać jak poniżej.
ProductService.getProductByCode($(this).val(), onProductChanged);
Aby całość dostępna była poprzez adres http://localhost:8080/dwr-example/product/search.html uzupełnię jeszcze plik dispatcher-servlet.xml.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <property name="prefix" value="/WEB-INF/jsp/" /> <property name="suffix" value=".jsp" /> </bean> <bean name="/product/search.html" class="org.springframework.web.servlet.mvc.UrlFilenameViewController" /> </beans>
Podsumowanie
Nieprzekonany?
DWR jest dojrzałym frameworkiem. W tym artykule główną uwagę skupiłem na jego prostocie wykorzystywania w kontekście aplikacji webowej budowanej na Springu. Pokazałem zaledwie od czego nasza zabawa z DWR może się rozpocząć. Dla projektantów i programistów bardzo ważnym aspektem natomiast jest elastyczność rozwiązań z których się korzysta oraz możliwość łatwego ich dostosowania do własnych potrzeb. Przeglądając oficjalną dokumentację szybko można się zorientować, że DWR w tej dziedzinie stara się spełniać stawiane mu oczekiwania.
Nie poruszyłem w tym artykule żadnego zaawansowanego aspektu DWR, ale wierzę, że jeżeli zdecydujesz się na korzystanie z tego frameworka, to dzięki dobrze napisanej dokumentacji nie będziesz miał problemu z jego dostosowaniem.








March 16th, 2008 at 23:02
Bracia Kaczyńscy zawsze przyciągają uwagę – przynajmniej mnie skusiły żeby zajrzeć dalej ;)
Super pomysł na artykuł i super artykuł!
j2ee.pl rulez!
March 18th, 2008 at 15:39
Mam mieszane uczucia. Kolejne biblioteki – engine.js 46Kb i util.js 46kB (nieobowiązkowa), czy nie wchodzą w konflikt np z jQuery, Prototype, albo script.aculo.us? Plus kolejne pliki konfiguracyjne…Nawet fajnie to wygląda (odpaliłem sobie Twój projekt), ale zastanawiam się, czy napisanie swojego serwletu do obsługi requestów jest aż tak skomplikowane, żeby wyręczać się DWR. Czy ktoś stosuje DWR w swoich projektach? Na stronce domowej nie znalazłem takich informacji.
Trochę zniechęciłem się do DWRa na javie 1.4, bo miało działać z paczki, a nie działało i skończyłem na grzebaniu w jarze, a sessionid było pobierane w sposób działający tylko dla jsessionid (cookie.substring(11, cookie.length))
P.S. Dzięki za pokazanie jetty :)
April 19th, 2008 at 19:18
Witam,
czy ktos wie moze jak rozwiązać problem w DWR kiedy używam
WebContext wctx = WebContextFactory.get();
return wctx.forwardToString(”moje.jsp”)
i mam problem z kodowaniem polskich znaków.
Bardzo prosze o pomoc!!!
Dzieki
May 13th, 2008 at 07:37
Artykół fajny ale po co nawiązywaleś do kaczynskiego ?? BO to trendy, Zajmij sie lepiej tym w czym jestes dobry czyli klepaniem a nie wyszydzaniem innych??
May 14th, 2008 at 08:25
Wydaje mi się, że wyszydzanie ex-premiera wychodzi Michałowi przynajmniej tak samo dobrze jak kodowanie. Można powiedzieć, że w obydwu rzeczach jest na prawdę dobry. Nie ma więc powodu dla którego miałby nie robić jednego albo drugiego ;)
October 24th, 2008 at 09:38
proponuje zjać się wyszydzaniem obecnego premiera, po co wyszydzać coś czego już nie ma?
November 28th, 2008 at 16:00
a co jeśli klasa Product zawierałaby jakąś kolekcję? sprawdzałeś ten przypadek?
December 2nd, 2008 at 14:28
Nie ma najmniejszego problemu z wykorzystywaniem kolekcji – sam wielokrotnie używałem list i zbiorów jako typów zwracanych przez metody serwisowe jak i składowe klas, które były zwracane.
May 8th, 2009 at 13:46
No tak, propaganda działa.
Jedyne co Jarosław Kaczyński powiedział, to to że “akt głosowania musi mieć godny wymiar”. Oczywiście użył on przytaczanych przez ciebie słów, ale w znaczeniu zupełnie innym niż to się próbuje imputować. Był to jedynie głos sprzeciwu wobec projektu umożliwienia głosowania przez Internet. Kaczyński nie twierdził że wszyscy, większość czy nawet znaczna liczba osób korzystających z internetu wykorzystuje go li tylko do konsumowania pornografii przy butelce piwa. Kaczyński stwierdził jedynie, że nie należy tworzyć rozwiązań które umożliwiają zaistnienie takich sytuacji, w których w wyborach prezydenckich czy do parlamentu głosuje się w przerwie między kolejnymi kielichami, pornosami czy tabloidami.
October 22nd, 2009 at 12:42
Witam,
ja w kilku projektach (nie za dużych) używałem jako szkieletu aplikacji Appfuse-a:
http://raibledesigns.com/wiki/Wiki.jsp?page=AppFuse
było to już jakiś czas temu więc nie miałem okazji sprawdzić nowszej wersji Appfuse 2.
Mimo to plecam jako dobry kickstart dla niezbyt wymagających aplikacji, dzięki temu mamy z głowy całą konfigurację (hibernate + spring itp). Mamy odpowiednia strukturę projektu (MVC) itp.
Jednym z dadkowych plusów jest wykorzystywanie DWR-a, specjalna stronka pozwalająca na testy itp.
Polecam i pozdrawiam.