tag:blogger.com,1999:blog-91852360336517507352024-03-05T00:09:56.942-08:00Java development thoughtsmilushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.comBlogger29125tag:blogger.com,1999:blog-9185236033651750735.post-22446654832637261962012-08-07T06:37:00.000-07:002012-08-07T06:37:53.813-07:00SOAP Webservice Client w CQ 5.4<br />
<br />
<br />
<br />
Ostatnio zajmowałem się uruchomieniem części klienckiej stosu SOAP WebServices na platformie CQ 5.4. Sama platforma oparta jest na <a href="http://felix.apache.org/site/index.html">Apache Felix</a>, <a href="http://sling.apache.org/site/index.html">Apache Sling</a>, oraz dodatkowych <i>bundle </i>stworzonych przez Day/Adobe. Pierwotnie próbowano uruchomić na CQ 5.4 stos<a href="http://cxf.apache.org/"> Apache CXF</a>, który to jest dostępny w wersji OSGI. Jednak duża ilość wymaganych zależności, oraz zależności do tych zależności itd spowodowała, że graf zależności rozrastał się niczym rak. Dodatkowo szukanie w internecie różnego typu <i>bundli </i> eksportujący dany pakiet nie jest najprzyjemniejszym zadaniem. Z racji tego, że nasze serwery instancje CQ są uruchomiane na SUN JRE spróbowałem uruchomić wbudowany stos JAX-WS - czyli tak naprawdę jakąś starszą wersję projektu <a href="http://jax-ws.java.net/">Metro</a>. <br />
<br />
Na potrzeby SOAP WebService korzystałem głównie z <a href="http://static.springsource.org/spring-ws/sites/2.0/">Spring WebServices</a> i po raz pierwszy uruchamiałem JAX-WS. Na potrzeby projektu potrzebowałem sparsować WSDL (przesłany nam emailem), wygenerować obiekty JAXB oraz <i>SOAP client stubs</i>, wysłać komunikat, odebrać odpowiedz i wypisać ją na konsole.<br />
<ol>
<li> Generowanie artefaktów JAX-WS
<br />
<pre class="brush: xml"> <properties>
<generatedsourcesfolder>src/main/generated</generatedsourcesfolder>
</properties>
<build>
<plugins>
<plugin>
<groupid>org.codehaus.mojo</groupid>
<artifactid>build-helper-maven-plugin</artifactid>
<version>1.7</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources> <source> ${generatedSourcesFolder}
</source></sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactid>maven-clean-plugin</artifactid>
<configuration>
<filesets>
<fileset>
<directory>${generatedSourcesFolder}</directory>
<followsymlinks>false</followsymlinks>
<includes>
<include>**</include>
</includes>
</fileset>
</filesets>
</configuration>
</plugin>
<plugin>
<groupid>org.jvnet.jax-ws-commons</groupid>
<artifactid>jaxws-maven-plugin</artifactid>
<version>2.2</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>wsimport</goal>
</goals>
</execution>
</executions>
<configuration>
<bindingdirectory>${basedir}/src/main/resources</bindingdirectory>
<xnocompile>true</xnocompile>
<xdebug>true</xdebug>
<sourcedestdir>${generatedSourcesFolder}</sourcedestdir>
<wsdldirectory>${basedir}/src/main/resources</wsdldirectory>
<target>2.0</target>
</configuration>
</plugin>
</plugins>
</build>
</pre>
w/w konfiguracja w pom.xml pozwala nam na wygenerowanie obiektów JAXB oraz <i>client stubs</i> w katalogu src/main/generated , który to będzie potraktowany przez Mavena jako dodatkowy katalog ze źródłami aplikacji (zawartość katalogu będzie czyszczona przy w fazie <i>clean </i>).<br />
Do katalogu src/main/resource wrzuciłem dwa pliki:<br />
<ul>
<li>myWsdl.wsdl - plik wsdl od klienta</li>
<li>binding.xml - który zawiera instrukcję na potrzeby JAXB w kontekście generowanie klasy java na podstawie <i>XML Schema</i>.
<pre class="brush: xml"><jxb:bindings version="2.1" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemalocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd">
<jxb:bindings node="//xsd:element[@name='GetRibizReferenceDataResponse']" schemalocation="myWsdl.wsdl#types?schema1">
<jxb:class name="someXmlElem">
</jxb:class></jxb:bindings>
</jxb:bindings>
</pre>
W przypadku gdy JAXB ma obrabiać elementy <i>XML Schema </i> znajdujące się wewnątrz pliku WSDL musimy wyspecyfikować lokalizację tych elementów za pomocą odpowiedniej składni: <położenie-pliku wsdl względem pliku xjb>#types?schema1. W naszym przypadku pliki wsdl oraz xjb leżą obok siebie czyli specyfikujemy <i>myWsdl.wsdl#types?schema1</i> . </li>
</ul>
Po odpaleniu <i>mvn clean compile</i> - powinniśmy mieć wygenerowane wszystkie artefakty na potrzeby łączenia się z naszym <i>WebServices</i>.
</li>
<li> Wywołanie <i>WebServices</i><br/>
Po wygenerowaniu wszystkie potrzebnych artefaktów możemy próbować wywołać nasze <i>WebServices</i>. Powinna zostać stworzona klasa z anotacją <i>@WebServiceClient</i> o nazwie takiej jak wartość atrybutu <i>name </i>elementu <i>service </i>z pliku wsdl. Klasa ta posłuży nam jako <i> entry point</i> do wołania usług. Standardowo kod kliencki JAX-WS wygląda mniej więcej tak:</li>
<ul>
<li>utwórz za pomocą <i>new</i> instancje klasy która jest oznaczona anotacją <i>@WebServiceClient</i></li>
<li>wywołaj na tej instancji metodę <i>get*</i>, która to zwróci <i>proxy </i>implementujące interfejs oznaczony annotacją <i>@WebService</i> - nazwa interfejsu odpowiada wartości atrybutu <i>name</i> elementu <i>port</i> który jest zawarty w elemencie <i>service</i></li>
<li>przygotowania komunikatu wejściowego za pomocą wygenerowanej klasy <i>ObjectFactory </i>(z anotacją <i>@XmlRegistry</i>), która służy jako fabryka obiektów JAXB</li>
<li>wywołanie odpowiedniej metody na proxy przekazując odpowiedni obiekt-komunikat wejściowy</li>
</ul>
W moim przypadku niestety musiałe wprowadzić kilka zmian:
<ol>
<li>zmiana adresu URL endpointa
<pre class="brush: java">
BindingProvider bindingProvider = (BindingProvider) <nasze proxy>;
bindingProvider.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointAddress);
</pre>
</li>
<li> W przypadku tworzenia instancji klasy z anotacją <i>@WebServiceClient</i> za pomocą bezparametrowego konstruktora, plik wsdl jest ładowany z lokalnego dysku - patrz na blok <i>static </i>w klasie z anotacją <i>@WebServiceClient</i>. Niestety jest z tym duży problem gdy uruchomimy nasz kod na innej maszynie od tej, na której to został on zbudowany. Można to rozwiązać na 2 sposoby (przynajmniej mi znane):
<ol>
<li>za pomocą JAX-WS catalog tak jak zostało to opisane <a href="http://stackoverflow.com/a/4178783">tu </a>. Takie podejście wymaga dodania pliku jax-ws-catalog.xml oraz dodania w naszym pom.xml elementu <i>wsdlLocation</i>. Rozwiązanie wydaję się jak najbardziej odpowiednie, ale jak się później przekonałem niestety nie działa w naszym systemie... Plik <i>katalogu </i> jest zaczytywany przez <i>thread's context classloader</i>, który to niestety go nie widzi. Pewnym obejście jest podmiana <i>thread's context classloadera</i> na czas wywołania <i>WebServices</i>, ale to rozwiązanie wydaje mi się zbyt inwazyjne</li>
<li>można skorzystać z alternatywnego konstruktora dla klasy z anotacją <i>@WebServiceClient</i> i samemu podać URL do pliku wsdl oraz <i>serviceName</i>. Ten drugi można skopiować ze stałych z klasy z anotacją <i>@WebServiceClient</i>. Jeśli chodzi o URL do pliku wsdl, to z racji tego że nasz wsdl znajduje się w <i>src/main/resources</i> to przy kompilacji zostanie przeniesiony do katalogu <i>target </i>i będzie dostępny na <i>classpath</i> - wystarczy wywołać </li>
<pre class="brush: java">
private URL getWsdlURL() {
ClassLoader classLoaderToLoadWsdlBy = getClassLoaderToLoadWsdlVia();
return classLoaderToLoadWsdlBy.getResource("myWsdl.wsdl");
}
private ClassLoader getClassLoaderToLoadWsdlVia() {
return <klasa z anotacja @WebServiceClient>.class.getClassLoader();
}
</pre>
</li>
</ol>
</ol>
<li>Wypisanie komunikatu wyjściowego na konsole<br/>
JAX-WS w dużej mierze ukrywa przez nami "dokumentową naturę SOAP WebServices". Dostarcza nam za to abstrakcje w postaci <i>proxy</i>, z którym to pracujemy w trybie <i>RPC </i>- wywołujemy metody przekazując parametr/parametry wejściowe i dostajemy wynik. W celach diagnostycznych możemy mieć potrzebę wypisać komunikat wyjściowy za pomocą <i>JAXB Marshaller</i>. Niestety obiekt, który dostaniemy w wyniku wywołania metody na <i>proxy </i>nie koniecznie może być zserializowany do XML przez JAXB - ze względu na brak anotacji <i>@XmlRootElement</i>. Rozwiązanie jest w miarę proste - wystarczy stworzyć odpowiedni<i> obiekt-wrapper</i>:
<pre class="brush: java">
private void unmarshallToSysout(Object response) throws JAXBException {
Marshaller m = buildMarshaller(response);
m.marshal(wrapWithRootElem(response), System.out);
}
private JAXBElement<Object> wrapWithRootElem(Object response) {
return new JAXBElement<Object>(new QName("http://www.namespace.com/schema/My",
"My"), (Class<Object>) response.getClass(), response);
}
private Marshaller buildMarshaller(Object response) throws JAXBException,
PropertyException {
JAXBContext context = JAXBContext.newInstance(response.getClass()
.getPackage().getName());
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.setProperty(javax.xml.bind.Marshaller.JAXB_ENCODING, "UTF-8");
return m;
}
</pre>
Alternatywnie, można dodać <i>system property com.sun.xml.internal.ws.transport.http.client.HttpTransportPipe.dump=true</i>
</li>
<li>Wywołanie WebServices z Cq 5.4<br/>
Wywołania <i>WebServices </i>powinny działać bez problemu w aplikacji <i>standalone </i>(np z <i>JUnit</i>), jednak nie działają na CQ 5.4. Aktualnie mam 3 rozwiązania tego problemu:
<ol>
<li>Dopisanie do sling.properties dodatkowego wpisu: sling.bootdelegation.com.sun=com.sun.*. Jest to <a href="http://scottsdigitalcommunity.blogspot.ch/2012/05/creating-adobe-cq-bundles-that-consume.html">metoda opisana dla CQ 5.5</a>,ale działa także dla CQ 5.4</li>
<li>przygotowanie specjalnego <i>fragment bundle</i>, który to eksportuje wszystkie pakiety z pakietu <i>com.sun</i> - jest ich prawie 400. W przypadku gdy chcemy wołać <i>WebServices </i>z poziomu <i>javy </i>a nie z poziomu <i>jsp </i>musimy ustawić w <i>bundle</i>, który woła <i>WebServices</i> odpowiednie <i>import-package</i> - albo ustawić mu wprost, że wymaga <i>system bundle</i>. To drugie rozwiązanie wydaję się mniej eleganckie, ale w tym przypadku łatwiejsze - wystarczy że w naszym pom.xml dodamy
<pre class="brush: xml">
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<Require-Bundle>system.bundle</Require-Bundle>
</instructions>
</configuration>
</plugin>
</pre>
</li>
<li>przygotowałem alternatywny stos JAX-WS, bazują na <i>webservices-rt-1.3.1.jar</i> (nowa wersja jax-ws-ri jest dostępna jako <i>bundle </i>OSGI, ale ma tyle zależności, że ciężko je wszystkie pościągać i poinstalować). Wykorzystanie tak przygotowanego stosu jest niestety dość niewygodne:
<ul>
<li><i>bundle, </i>który wywołuje <i>WebServices </i>musi mieć ustawiony <i>Require- Bundle</i> na <i>bundle </i>stosu,</li>
<li>przy wywołaniu <i>WebServices </i>ustawienie <i>thread's context classloadera</i> na ten, który ładuje/ma dostęp do klas stosu, czyli na przykład ten co załadował klasę: <i>com.sun.xml.ws.spi.ProviderImpl</i></li>
</ul>
</li>
</ol>
</ol>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-23807880955663633112012-05-07T01:26:00.002-07:002012-09-10T02:46:21.734-07:00DependencyInjectionJunitRulesTestExecutionListenerOstatnio strasznie spodobały mi się rozszerzenia do <a href="http://kentbeck.github.com/junit/javadoc/latest/org/junit/Rule.html">@Rule</a> do junit. Pomimo tego że widzę pewne problemy ze stabilnością całego mechanizmu (jak np, zamiana kolejności uruchamiania @Rule a metod <i>@Before</i> i <i>@After</i>) i kilku braków ( m.in. nie działają @Rule zdefiniowane na poziomie <i>suite</i> - dla klas z <a href="http://junit.sourceforge.net/javadoc/org/junit/runner/RunWith.html">@RunWith</a> (? extend <a href="http://www.blogger.com/%20http://kentbeck.github.com/junit/javadoc/latest/org/junit/runners/Suite.html">Suite</a>) ) ) to rozwiązanie jest na prawdę super.<br />
Dzięki wykorzystaniu <i>@Rule</i> możemy bardzo ładnie enkapsulować logikę związaną z wykonaniem testów ,np. <a href="http://blog.mycila.com/2009/11/writing-your-own-junit-extensions-using.html">współbieżne wykonanie metody testowej</a> , modyfikowanie/zawieszanie metod testowych. Mechanizm jest na tyle uniwersalny, że w przyszłości annotacje <i>@Before/@After</i> maja zostać uznane za <i>deprecated</i> na rzecz wykorzystania <i>@Rule</i>. Aktualnie junit w wersji 4.10 wymaga aby anotacja @Rule była umieszczona na publicznym nie statycznym <i>property </i>typu <a href="http://kentbeck.github.com/junit/javadoc/latest/org/junit/rules/TestRule.html">TestRule</a> lub <a href="http://kentbeck.github.com/junit/javadoc/latest/org/junit/rules/MethodRule.html">MethodRule</a> (<i>deprecated</i>). Co ciekawe wygląda na to, że <i>@Rule</i> działają bardzo dobrze z <i>Spring</i> <i>TestContext Framework</i> (przynajmniej dla junit-4.10 oraz SpringJUnit4ClassRunner z wersji springa 3.1.1). Integracja może wyglądać w dwojaki sposób:<br />
<ul>
<li>DI dla implementacji interfejsu TestRule</li>
<li>TestRule zdefiniowane bezpośrednio w spring</li>
</ul>
W pierwszym przypadku stworzyłem bardzo prosty <i>listener</i> który <i>injectuje beany </i>do @Rule i <a href="http://kentbeck.github.com/junit/javadoc/latest/org/junit/ClassRule.html">@ClassRule</a>:<br />
<pre class="brush: java">public class DependencyInjectionJunitRulesTestExecutionListener extends
AbstractTestExecutionListener {
@Override
public void prepareTestInstance(final TestContext testContext)
throws Exception {
Object testInstance = testContext.getTestInstance();
List<TestRule> ruleFields = getRuleFields(testInstance);
doInjection(testContext, ruleFields);
}
private void doInjection(final TestContext testContext,
List<TestRule> ruleFields) {
AutowireCapableBeanFactory beanFactory = testContext
.getApplicationContext().getAutowireCapableBeanFactory();
for (TestRule testRule : ruleFields) {
beanFactory.autowireBeanProperties(testRule,
AutowireCapableBeanFactory.AUTOWIRE_NO, false);
beanFactory.initializeBean(testRule, testContext.getTestClass()
.getName());
}
}
private List<TestRule> getRuleFields(Object testInstance) {
TestClass testClass = new TestClass(testInstance.getClass());
return testClass.getAnnotatedFieldValues(testInstance, Rule.class,
TestRule.class);
}
@Override
public void beforeTestClass(TestContext testContext) throws Exception {
List<TestRule> classRuleFields = getClassRuleFields(testContext);
doInjection(testContext, classRuleFields);
}
private List<TestRule> getClassRuleFields(TestContext testContext) {
TestClass testClass = new TestClass(testContext.getTestClass());
return testClass.getAnnotatedFieldValues(null, ClassRule.class,
TestRule.class);
}
}</pre>
Implementacja bazuję na dostępnym out-of-box <a href="http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/test/context/support/DependencyInjectionTestExecutionListener.html">DependencyInjectionTestExecutionListener</a> oraz <a href="http://kentbeck.github.com/junit/javadoc/latest/org/junit/runners/model/TestClass.html">TestClass</a>. Wszystko wygląda nieźle, ale niestety nie do końca działa :(. Brakuję na pewno <i>reinjectowania beanów</i> dla @ClassRule w przypadku gdy nasza metoda/klasa używa <a href="http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/test/annotation/DirtiesContext.html">@DirtiesContext</a>. Implementacja nie powinna być trudna gdyż <a href="http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/test/context/support/DirtiesContextTestExecutionListener.html">DirtiesContextTestExecutionListener</a> ustawia odpowiednią <a href="http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/test/context/support/DependencyInjectionTestExecutionListener.html#REINJECT_DEPENDENCIES_ATTRIBUTE">flagę </a>na <a href="http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/test/context/TestContext.html">TestContext</a> (niestety ta flaga jest usuwana przez DependencyInjectionTestExecutionListener, czyli trzeba albo nasz <i>listener</i> ustawić przed <i>DependencyInjectionTestExecutionListener</i> albo rozszerzyć ten ostatni).<br />
Większym problem jest jednak to, że aktualnie dla Spring 3.1.1.RELEASE odpowiednie metody klasy <a href="http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/test/context/TestExecutionListener.html">TestExecutionListener</a>, a dokładnie metoda<a href="http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/test/context/TestExecutionListener.html#beforeTestClass%28org.springframework.test.context.TestContext%29"> beforeTestClass</a> jest wywoływana po @ClassRule. "Winna" temu jest metoda <a href="http://kentbeck.github.com/junit/javadoc/latest/org/junit/runners/ParentRunner.html#classBlock%28org.junit.runner.notification.RunNotifier%29">classBlock</a> w klasie ParentRunner, po której to dziedziczy <a href="http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/test/context/junit4/SpringJUnit4ClassRunner.html">SpringJUnit4ClassRunner.</a> Zgodnie z tym co można znaleźć w <a href="http://kentbeck.github.com/junit/javadoc/latest/org/junit/ClassRule.html">javadoc</a> @ClassRule opakowują uruchomienie <a href="http://kentbeck.github.com/junit/javadoc/latest/org/junit/BeforeClass.html">@BeforeClass</a>/<a href="http://kentbeck.github.com/junit/javadoc/latest/org/junit/AfterClass.html">@AfterClass</a>, a te z kolei <i>SpringJUnit4ClassRunner</i> opakowuje w wywołanie<i> TestExecutionListener#beforeTestClass</i>. Reasumując: najpierw zostaną uruchomiene <i>@ClassRule</i>, następnie <i>TestExecutionListener#beforeTestClass</i> a na końcu metody <i>@BeforeClass</i>. Metody definiujące zadaną kolejność oraz wywołania są <i>protected</i>, czyli wygląda na to, że zmiana jest jak najbardziej do zrobienia. W innym przypadku, tzn gdy nie potrzebujemy <i>injectowania</i> dla <i>@ClassRule</i> z przedstawionego powyżej kodu można spokojnie wyrzucić metodę <i>beforeTestClass.</i><br />
Jeśli chodzi o drugie podejście to sprawa wygląda całkiem podobnie. Tzn. w fazie <i>TestExecutionListener#prepareTestInstance</i> jest wykonywane <i>DI</i>, które to może <i>injectować</i> do <i>property</i> z anotacją <i>@Rule</i>. Jeśli chodzi o <i>@ClassRule</i> to DI dla pól statycznych nie działa <i>by design.</i> Są pewne obejścia, ale chyba bez jakiegoś większego <i>plumbingu </i>się nie obejdzie.
<br/>
EDIT: aktualnie pojawiła się potrzeba stworzenia odpowiedniego <i>test fixture</i>, który to byłby dostępny dla kilku metod testowych. Można to łatwo zaimplementować jako <i>TestRule</i> albo metodę <i>@Before</i>, jednak ze względu na to, że tworzenie tego <i>fixure</i> może być czasochłonne lepiej zrobić to tylko raz. W szczególności chodzi o stworzenie i "ustawienie" stanu odpowiedniego bytu w systemie przy wykorzystaniu<a href="http://seleniumhq.org/"> Selenium (Webdriver)</a> aby testy w danej klasie mogły bazować na tak przygotowanym bycie. W przypadku JUnit najsensowniejsze wydaje się wykorzystanie <i>@BeforeClass</i>. W moim przypadku instancja <i>WebDrivera</i> jest <i>injectowana</i> przez <i>Spring TestContext Framework</i>, a skoro <i>injectowanie</i> do pól statycznych (tak aby były do użycia przez <i>@BeforeClass</i>) nie działa out-of box zdecydowałem się na to aby tworzyć <i>test fixture</i> za pomocą <i>ClassRule</i> - <i>DI</i> dla <i>ClassRule</i> zostało zaimplementowane w DependencyInjectionJunitRulesTestExecutionListener#beforeTestClass. Problemem jest tylko to, że w aktualnej implementacji <i>Spring TestContext Framework </i>bazuje na Junit 4.5, czyli <i>SpringJUnit4ClassRunner</i> uruchamia TestExecutionListener#beforeTestClass przed <i>@BeforeClass</i>, ale po odpaleniu <i>ClassRule</i>. Zmiana tej kolejności okazała się stosunkowo prosta, ale potrzebowałem stworzyć nowy <i>Runner</i>:
<pre class="brush: java">
public class RunSpringAroundClassRulesJUnit4ClassRunner extends
SpringJUnit4ClassRunner {
public static class AroundTestClassCallbacks extends Statement {
private final Statement statement;
public AroundTestClassCallbacks(Statement next,
TestContextManager testContextManager) {
this.statement = new RunAfterTestClassCallbacks(
new RunBeforeTestClassCallbacks(next, testContextManager),
testContextManager);
}
@Override
public void evaluate() throws Throwable {
statement.evaluate();
}
}
public RunSpringAroundClassRulesJUnit4ClassRunner(Class<?> clazz)
throws InitializationError {
super(clazz);
}
@Override
protected Statement classBlock(final RunNotifier notifier) {
Statement statement = childrenInvoker(notifier);
statement = withBeforeClasses(statement);
statement = withAfterClasses(statement);
statement = withClassRules(statement);
statement = withAroundTestClass(statement);
return statement;
}
private Statement withAroundTestClass(Statement statement) {
return new AroundTestClassCallbacks(statement, getTestContextManager());
}
@Override
protected Statement withBeforeClasses(Statement statement) {
List<FrameworkMethod< befores = getTestClass().getAnnotatedMethods(
BeforeClass.class);
return befores.isEmpty() ? statement : new RunBefores(statement,
befores, null);
}
@Override
protected Statement withAfterClasses(Statement statement) {
List<FrameworkMethod> afters = getTestClass().getAnnotatedMethods(
AfterClass.class);
return afters.isEmpty() ? statement : new RunAfters(statement, afters,
null);
}
private Statement withClassRules(Statement statement) {
List<TestRule> classRules = classRules();
return classRules.isEmpty() ? statement : new RunRules(statement,
classRules, getDescription());
}
}
</pre>
milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-19823427655689468002011-05-16T12:29:00.000-07:002011-05-25T06:59:23.665-07:00GeeCon 2011 UniversityDayPodczas pierwszego <span style="font-style: italic;">slotu</span> przemieszczałem się pomiędzy "Spring into the Next Decade" a "Paradoxes of API Design". Oryginalnie chciałem iść na Spring, ale jak się okazało Josh Long zdecydowanie nie miał pomysłu na aż 3 godzinną prezentacje. W efekcie przeprowadził ją jako kompilacje z kilku krótszych opuszczając przy tym kilka szczegółów. Przy omawianiu podstaw konfiguracji <span style="font-style: italic;">Spring Framework</span> przeniosłem się obok posłuchać o API. Prezentacja była dość ciekawa, ale trochę zbyt teoretyczno-filozoficzna (podobno w dalszej części powiało trochę praktyką). Jaroslav opowiadał o tym, że wiedzę o tym jak tworzyć API czerpiemy głównie ze szkoły i środowiska, w kŧórym pracujemy. Dokonał podziału potencjalnych użytkowników naszego API na: <span style="font-style: italic;">rationalist</span> (ci którzy zanim zaczną działać najpierw pogłębiają swoją wiedzę o API przez czytanie dokumentacji, książek, artykułów), <span style="font-style: italic;">empirist</span> (ci którzy uczą się poprzez prototypownie robione "rozpoznanie ogniem") oraz <span style="font-style: italic;">clueless</span> (ci co nie mają zbytnio pojęcia i nie mają czasu/nie chcą się uczyć). Zdecydowaną większość mają stanowić ci ostatni. Szczególnie dla nich przygotowywana jest dokumentacja w formie gotowych <span style="font-style: italic;">use-cases</span>, które mają poprowadzić ich jak po sznurku do realizacji najczęstszych scenariuszy. Przy tworzeniu API bardzo ważne jest zachowanie spójności (np. sposób konfiguracji związanej z zaczytaniem konfiguracji z pliku czy z BD ma być identyczny) oraz projektowanie w taki sposób aby ewentualnie umożliwić późniejsze zmiany zachowując kompatybilność. Jaroslav następnie omówił różne poziomy kompatybilności (np. na poziomie kodu źródłowego, na poziomie binarnym) oraz potencjalne <span style="font-style: italic;">gotchas</span> z nimi związane. W tym przydługawym momencie wróciłem na prezentacje o Spring i tu powoli zaczęło się coś dziać. Josh omawiał dostępne <span style="font-style: italic;">scope</span> komponentów (<span style="font-style: italic;">conversation/flow/view web scopes</span>, oraz <span style="font-style: italic;">process scope</span> wykorzystywany przez <a href="http://www.activiti.org/">ACTIVITI BPMN 2</a>). Następnie omówił kilka infrastrukturalnych mechanizmów Springa: <span style="font-style: italic;">BeanPostProcessor</span> (użyteczne do modyfikowania/opakowywania <span style="font-style: italic;">beanów</span>), <span style="font-style: italic;">BeanFactoryPostProcessor</span> (modyfikowanie definicji <span style="font-style: italic;">beanów</span>, dodawanie <span style="font-style: italic;">beanów</span>, zaczytywanie konfiguracji <span style="font-style: italic;">beanów</span> z alternatywnych źródeł, np. BD), <span style="font-style: italic;">FactoryBean</span> (bardziej złożone tworzenie beanów, enkapsulacja potencjalnie złożonego kodu tworzącego <span style="font-style: italic;">beana</span>). Oprócz teorii pokazał kilka przykładów wykorzystania w/w struktur: <span style="font-style: italic;">CorporationCodeStandardsAwareBeanFactoryPostProcessor, AutoToStringBeanPostProcessor, StartupAwareBeanPostProcessor, AutoToStringFactoryBean, ConditionalDelegatingPlatformTransactionManagerFactoryBean</span>. Wszystkie przykłady kodu mają być dostępne w<a href="http://git.springsource.org/spring-samples/"> git.springsource.org/spring-samples/spring-samples.git</a>. W końcu pojawiło się trochę kodu i nowości ze Spring 3.0/3.1: <span style="font-style: italic;">environmental beans</span>, Spring EL (dostęp do <span style="font-style: italic;">SystemProperties</span>, wywołanie statycznej metody ), użycie <span style="font-style: italic;">AnnotationConfigApplicationContext</span>. Ważną zmianą w wersji 3.0 jest przeniesienie mechanizmu <span style="font-style: italic;">OXM</span> (<em>Object</em>/<em>XML Mapping</em>) z projektu <span style="font-style: italic;">Spring Web Services</span> do <span style="font-style: italic;">Spring Framework</span>. Dzięki temu mamy spójny mechanizm do wykorzystania w isntniejących i nowych projektach/modułach: <span style="font-style: italic;">Spring Batch, Spring Integration, Spring MVC, Spring REST</span>. Spring 3.0 dostarcza także prosty mechanizm związany z <span style="font-style: italic;">cachowaniem</span>. Całość jest zrealizowana za pomocą <span style="font-style: italic;">interceptorów</span> i nie wygląda na specjalnie skomplikowaną. Spring dostarcza <span style="font-style: italic;">out-of-box</span> implementacje, które jako <span style="font-style: italic;">cache</span> wykorzystują <span style="font-style: italic;">Oracle Coherence, GemFire lub Ehcache</span>. Ciekawy jest mechanizm wskazywania w annotacji w jaki sposób ma być tworzony klucz do cache (@Cacheable(key = "#p0")) - kluczem będzie pierwszy paramter wywołania metody). Josh następnie w kilku słowach przedstawił projekt <a href="http://www.springsource.org/spring-data"><span style="font-style: italic;">Spring Data</span></a>, który wprowadza rozszerzenia dla istniejących mechanizmów DAO (np, <a href="http://blog.springsource.com/2011/02/10/getting-started-with-spring-data-jpa/" rel="bookmark" title="Permanent Link">Spring Data JPA</a> dla JPA), ale przede wszystkim ma być "parasolem" technologii dostępu do NoSQL datastore. Projekt jest jak najbardziej rozwojowy i nie wspiera jeszcze wielu istniejących <span style="font-style: italic;">datastore</span>, ale mam nadzieję że jest to tylko kwestia czasu. Josh pokazał przykład integracji z <a href="http://redis.io/">Redis</a> (używany przez <a href="http://stackoverflow.com/">http://stackoverflow.com/</a>) za pomocą <a href="http://static.springsource.org/spring-data/data-keyvalue/docs/1.0.x/api/org/springframework/data/keyvalue/redis/core/StringRedisTemplate.html">StringRedisTemplate</a>. Nie wiem czy ze względu na to że było to jedynie demo, ale sam kod był trochę pokręcony, bo przy zapisywaniu/odczytywaniu obiektu <span style="font-style: italic;">Customer (id, firstName, LastName)</span> wykonywaliśmy operację na dwóch kluczach: <span style="font-style: italic;">customer:fn:<id> oraz customer:ln:<id></id></id></span>. Wydaję mi się, że w <span style="font-style: italic;">Redis</span> można jako <span style="font-style: italic;">value</span> przechowywać <span style="font-style: italic;">list/set</span> wartości. Dodatkowo na potrzeby generowania kolejnych identyfikatorów trzymaliśmy dodatkowe <span style="font-style: italic;">entry</span> (z kluczem <span style="font-style: italic;">customerId</span>), które było inkrementowane przy zapisywaniu nowej instancji <span style="font-style: italic;">Customer</span>. Kolejne na liście "<span style="font-style: italic;">what's new in Spring 3.0</span>" zostały omówione mechanizmy związane z <span style="font-style: italic;">lifecycle/scheduling/ thread pool/ asynchronous support</span>. Oczywiście nie obyło się bez pokazu <span style="font-style: italic;">abstraction of services</span> i użycia <span style="font-style: italic;">@Transactional</span> i spójnego zarządzania transakcjami niezależnie od użytego <span style="font-style: italic;">TransactionManager</span>. Następnie Josh zaczał omawiać kilka projektów należących do <span style="font-style: italic;">Spring Portfolio</span> koncentrując się na najlepiej znanych sobie: <span style="font-style: italic;">Spring Batch</span> i <span style="font-style: italic;">Spring Integration</span>. Szczególnie ciekawie wygląda ten drugi, który ma być implementacją wzorców z książki <a href="http://www.eaipatterns.com/">Enterprise Integration Patterns</a>. Największe wrażenie zrobiła na mnie lista adapterów, których było chyba z 20 (np, umożliwiające dostęp za pomocą <span style="font-style: italic;">POP3, IMAP, Twitter, XMPP, File, FTP, HTTP, JMS, AMQP</span>). Josh zaczął się trochę rozwodzić na temat <span style="font-style: italic;">AMQP</span> i RabbitMQ, w którego to implementacje ma być mocno zaangażowana ekipa ze SpringSource. Podobno <span style="font-style: italic;">RabbitMQ</span> ze względu na szybkość i niezawodność jest częściej wybierany na depolymentach na EC2 od dedykowanego Amazon SQS. Nie obyło się, też bez kilku przykładów związanych z wczytywaniem "dużego" pliku csv do bazy danych za pomocą <span style="font-style: italic;">Spring Batch</span>. Przykład był dość trywialny, ale całkiem sensownie wygląda możliwość śledzenia stanu procesu oraz ewentualnych problemów w dedykowanych tabelach utworzonych przez <span style="font-style: italic;">Spring Batch</span>. Mnie najbardziej przekonało rozwiązanie combo w którym to <span style="font-style: italic;">Spring Integration</span> monitoruje dany katalog na dysku, a w przypadku pojawienia się nowego pliku, odpala jego przetwarzanie za pomocą <span style="font-style: italic;">Spring Batch</span>. Pomimo kilku prób nie udało się odpalić na emulatorze projektu wykorzystującego <a href="http://www.springsource.org/spring-android">Spring Android</a>, lepiej poszło z <a href="http://www.springsource.org/spring-flex">Spring Blazde DS</a>, ale ze względu na brak czasu nie udało się pokazać <a href="https://github.com/SpringSource/spring-hadoop">Spring Hadoop</a> oraz <a href="http://www.springsource.org/spring-social">Spring Social</a>. Osobiście żałowałem, że nie udało się przedstawić i omówić działań <span style="font-style: italic;">SpringSource</span> w obszarze <span style="font-style: italic;">cloud computing</span>. Jedyna informacja jaką wyłapałem to ta, że <span style="font-style: italic;">CloudFoundry</span> z produktu związanego z <span style="font-style: italic;">Amazon AWS</span> stało się platformą, która ma być niezależna od <span style="font-style: italic;">cloud provider</span> i (w przyszłości?) umożliwiać uruchamianie (dla celów testowych/demonstracyjnych) <span style="font-style: italic;">cloud applications</span> na maszynie deweloperskiej. W tym celu powstało <a href="http://www.cloudfoundry.com/">CloudFoundry.com</a>, <a href="http://www.cloudfoundry.org/">CloudFoundry.org</a> a w przyszłości <span style="font-style: italic;">Cloud Foundry Micro Cloud</span>. Więcej na temat tych projektów można znaleźć <a href="http://www.cloudfoundry.com/faq">tu</a>. <span style="font-style: italic;">SpringSource</span> uruchomił swój kanał na <a href="http://www.youtube.com/user/SpringSourceDev">YouTube</a>, na którym mają być wrzucane ich materiały. Dodatkowo warto przejrzeć <a href="http://blog.springsource.com/category/green-beans/">Green Beans</a>.<br /><br />"Patterns and Anti-Patterns in Hibernate" by Patrycja Węgrzynowicz - no prezentacja była dość przeciętna. Tematy, które zostały poruszone były całkiem ciekawe, ale było ich zdecydowanie za mało na 3 godziny. Na sam początek się trochę spóźniłem i trafiłem na omówienie rozwiązania problemu współbieżnego dostępu do danych w aplikacji <span style="font-style: italic;">CaveatEmptor</span> z "Hibernate In Action". Problemem ma być implementacja funkcjonalności dodawania nowego bida. Algorytm jest taki, że przy dodawaniu nowego <span style="font-style: italic;">bida</span>, sprawdzamy czy jest on większy od aktualnie najwyższej oferty. Implementacja jest następująca:<br /><pre class="brush: java">public someMethod(){<br />Bid currentMinBid = itemDao.getMinBid(itemId);<br />Bid currentMaxBid = itemDao.getMaxBid(itemId);<br />//lock for Upgrade<br />Item item = itemDao.getItemById(itemId, true);<br /><br />Bid newBid = item.placeBid(userDao.findById(userId), newAmount,currentMaxBid, currentMinBid);<br />}<br /></pre><br /><br />Patrycja pokazała, że może dojść do sytuacji, w której to będą składane jednocześnie 2 <span style="font-style: italic;">bidy</span> (oba większe od aktualnie najwyższego <span style="font-style: italic;">bida</span>): 1000 i 1002. W specyficznym scenariuszu może dojść do tego, że <span style="font-style: italic;">bid</span> o wartości 1002 zostanie "wypchnięty" (każdy z wątków pobierze <span style="font-style: italic;">maxBid</span> o wartośći 700, wątek <span style="font-style: italic;">bid-1002</span> zablokuję krotke, zmieni wartość na 1002 i zrobi <span style="font-style: italic;">commit</span>, w tym czasie wątek <span style="font-style: italic;">bid-1000</span> będzie porównywać wartość 1000 z wartością 700, bo dla niego aktualnie najwyższy <span style="font-style: italic;">bid</span> został wyliczony wcześniej, bid 1000 zostanie przyjęty i zostanie wykonany <span style="font-style: italic;">commit</span>). W ten sposób <span style="font-style: italic;">bid</span> o wartości 1000 zostanie dodany po <span style="font-style: italic;">bid</span> wartości 1002. Ma to szczególnie znaczenie gdy mapujemy <span style="font-style: italic;">bidy</span> jako listę a nie zbiór.<br />Proponowane rozwiązania to: użycie wersjonowania (rozumiem że chodzi o <span style="font-style: italic;">optimistic locking</span>), ustawienie poziomu izolacji na <span style="font-style: italic;">REPEATABLE READ</span>, ustawienie poziomu izolacji na <span style="font-style: italic;">SERIALIZABLE</span>, zmiana kolejności instrukcji w kodzie. Pierwsze dwa rozwiązania mają być nieskuteczne. Jeśli chodzi o poziomy izolacji transakcji to sprawa jest tym bardziej ciekawa, gdyż niektóre bazy danych,np <span style="font-style: italic;">ORACLE</span> nie implementują wszystkich poziomów, a nawet jeśli implementują, to implementacje mogą się od siebie różnić. Przykład rozwiązania, które nie działa, był oparty na <span style="font-style: italic;">MySql</span> i pokazywał, że w momencie wydawania zapytania o <span style="font-style: italic;">item</span> <span style="font-style: italic;">MySQL</span> ma robić <span style="font-style: italic;">snapshot</span> danych i reużywać go przy kolejnych zapytaniach. W wyniku takiego działania, końcowy rezultatem ma być dodanie 2 krotek do tabeli <span style="font-style: italic;">bidów</span>, ale z tymi samymi wartościami w kolumnie definiującej pozycje na liście - przy następnym odczycie <span style="font-style: italic;">bidów</span> <span style="font-style: italic;">Hibernate</span> ma mieć problem z ich poprawnym załadowaniem. Chyba nie do końca trafia do mnie te wytłumaczenie. Szczególnie, że spodziewam się, że MySQL zrobi <span style="font-style: italic;">snapshot</span> dopiero po uzyskaniu <span style="font-style: italic;">locka</span>. W takim przypadku <span style="font-style: italic;">snapshot</span> zrobiony jako drugi powinien widzieć zmiany zrobione w właśnie zakończonej transakcji. Nawet jeśli moje rozumowanie jest poprawne to chyba i tak nie da się w ten sposób rozwiązać oryginalnego problemu. Jeśli chodzi o <span style="font-style: italic;">SERIALIZABLE</span> to rozwiązanie ma działać, ale chyba nie podejmę się dokładnej analizy. <span style="font-style: italic;">SERIALIZABLE</span> zawsze automatycznie kojarzy mi się z problemem braku skalowalności i jest przyjmowane "z założenia" jako zbyt ciężkie.<br />Jeśli chodzi o wersjonowanie to dodanie mechanizmu <span style="font-style: italic;">versioning</span> bez zmiany kodu nic się nie zmieni. Wynika to z tego, że druga transakcja uzyska <span style="font-style: italic;">lock </span>po wykonaniu <span style="font-style: italic;">commit</span> pierwszej, czyli już będzie miała <span style="font-style: italic;">zupdatowany</span> licznik wersji. Użycie <span style="font-style: italic;">samego optimistic locking</span> bez lockowania, też nie pomoże (wątek <span style="font-style: italic;">bid-1000 </span>odczyta "stare" <span style="font-style: italic;">min/max bid</span> , w tym momencie wykonany będzie <span style="font-style: italic;">commit</span> transakcji wątku <span style="font-style: italic;">bid-1002</span>, <span style="font-style: italic;">wątek bid-1000</span> odczyta item z wersją ustawioną przez wątek <span style="font-style: italic;">bid-1002</span>, ale wykona porównanie ze "starymi" <span style="font-style: italic;">min/max bid</span>). Rozwiązaniem jest zmiana kodu poprzez zmiane kolejności wywołań na:<br /><br /><pre class="brush: java">public someMethod(){<br />//lock for Upgrade<br />Item item = itemDao.getItemById(itemId, true);<br />Bid currentMinBid = itemDao.getMinBid(itemId);<br />Bid currentMaxBid = itemDao.getMaxBid(itemId);<br /><br />Bid newBid = item.placeBid(userDao.findById(userId), newAmount,currentMaxBid, currentMinBid);<br />}<br /></pre><br />W tym przypadku najpierw <span style="font-style: italic;">lockujemy</span> a dopiero później wykonujemy kolejne operacje.Wydaję mi się, że taki kod byłby poprawny gdybyśmy zrezygnowali z <span style="font-style: italic;">lockowania</span> krotki <span style="font-style: italic;">item</span>, użyli jedynie <span style="font-style: italic;">optimistic locking</span>, przy jednoczesnym <span style="font-style: italic;">updatowaniu item</span> przy dodawaniu <span style="font-style: italic;">bida</span>. Ten ostatni krok, ma jak najbardziej wartość biznesową - możemy trzymać tam datę ostatniego <span style="font-style: italic;">bidowania</span>. W przypadku składania dwóch jednoczesnych <span style="font-style: italic;">bidów</span> jedna z operacji zakończy się wyjątkiem <a href="http://docs.jboss.org/hibernate/core/3.5/api/org/hibernate/OptimisticLockException.html">OptimisticLockException</a>. Wyjątek ten można (najlepiej za pomocą <span style="font-style: italic;">AOP</span>) złapać a całą operację powtórzyć.<br /><br />Patrycja wskazała, też alternatywne rozwiązania: obliczanie najwyższego <span style="font-style: italic;">bid</span> programowo (wymaga załadowania całej listy <span style="font-style: italic;">bidów</span>), redundentne przechowywanie najwyższego <span style="font-style: italic;">bid</span> w <span style="font-style: italic;">item</span> (denormalizacja BD, ale szybki dostęp przy ponoszeniu kosztów utrzymywania kopii informacji), wykorzystanie zaawansowanych możliwości związanych z mapowaniem. Ten ostatni pomysł wykorzystuję dość rzadko używane <span style="font-style: italic;">feature</span> <span style="font-style: italic;">Hibernate</span> i polega na tym, że do <span style="font-style: italic;">item</span> dodajemy dodatkowe <span style="font-style: italic;">property</span> wraz z mappingiem, dla którego jest wyspecyfikowane zapytanie(Embedded Query) definiujące to <span style="font-style: italic;">property</span>. Zapytanie to ma mieć możliwość korzystania z <span style="font-style: italic;">order by, where, groupBy</span>. Dodatkowo, dostęp do <span style="font-style: italic;">property</span> możemy określić jako lazy/eager (w tym przypadku chyba najsensowniejsze jest określenie tego <span style="font-style: italic;">property</span> jako <span style="font-style: italic;">set</span> z <span style="font-style: italic;">fetch =FetchType.Lazy</span>). Następnie zostały przedstawione 2 dość proste błędy w kodzie <span style="font-style: italic;">CaveatEmptor</span>, aby w końcu przejść do większych problemów związanych z aplikacjami używającymi <span style="font-style: italic;">Hibernate</span> a w ogólności dowolnego <span style="font-style: italic;">ORM</span>. Chodzi tu o problem <span style="font-style: italic;">Anaemic Domain Model</span>. Rozpoczęło się od próby zdefiniowania OOP i przedstawieniu podstawowych ( <span style="font-style: italic;">encapsulation/inheritance/polimorphism</span> ) oraz zaawansowanych (<span style="font-style: italic;">SOLID</span>) zasad świata <span style="font-style: italic;">OOP</span>. W <span style="font-style: italic;">CaveatEmptor </span>brak enkapsulacji jest bardzo widoczny co może spowodować wzrost ilości bugów (szczególnie w stylu czemu coś się dodało/usunęło z bazy) oraz problemy z <span style="font-style: italic;">maintainance</span>. Rozwiązaniem ma być używania odpowiednich modyfikatorów dostępu, najlepiej mappowanie <span style="font-style: italic;">fields</span> zmiast <span style="font-style: italic;">properties</span>, <span style="font-style: italic;">defensive copying </span><span>(w przypadku kolekcji skutkuje to pobraniem <span style="font-style: italic;">lazy mapped collection</span> z BD)</span>, <span style="font-style: italic;">unmodifiableCollections </span>(możliwy problem przedstawiony w przykładzie poniżej)<span style="font-style: italic;">.</span> Ważne jest dodatkowo zrozumienie w jaki sposób działa mechanizm <span style="font-style: italic;">dirty checking</span> w <span style="font-style: italic;">Hibernate</span>, szczególnie w stosunku do kolekcji <span style="font-style: italic;">entity</span> i <span style="font-style: italic;">embedded objects</span> - należy re-używać kolekcje które zostały stworzone/wypełnione przez Hibernate zamiast tworzyć nowe kolekcje (<span style="font-style: italic;">dirty checking</span> ma wykorzystywać <span style="font-style: italic;">identity of collections</span> w celu stwierdzenia czy kolekcja uległa zmianie). Patrycja pokazała dość podchwytliwy przykład:<br /><pre class="brush: java">public List<bids> getBids(){ return Collections.unmodifiableList(bids);}<br />public void setBids(List<bid> newBids){<br />bids.clear();<br />if(newBids != null){<br />bids.addAll(newBids);<br />}<span style="font-style: italic;"><br />}<br /></span></bid></bids></pre><br />W momencie gdy klient wywołałby:<br /><pre class="brush: java">bids = getBids();<br />setBids(bids);<br /></pre><br />to z bazy mogą zostać usunięte określone <span style="font-style: italic;">bidy</span>. Problem jest w tym, że <span style="font-style: italic;">Collections.unmodifiableList()</span> stworzy nam <span style="font-style: italic;">wrapper</span> opakowujący podaną mu w parametrze listę, ale wszystkie zmiany na źródłowej liście są odzwierciedlane we <span style="">wrapperze</span>. W rezultacie do <span style="font-style: italic;">bids</span> zostanie dodana pusta lista. Następne na tapetę trafiły problemy wydajnościowe Hibernate. Byłem bardzo zdziwiony, że nie zostały zbytnio poruszone problemy: <span style="font-style: italic;">n+1</span>i <span style="font-style: italic;">cartesian product</span>. Patrycja pokazała 2 przypadki:<br /><ol><li>nadpisywanie listy:<br /><pre class="brush: java">A a = (A)session.get(A.class,2);<br />a.setBs(a.getBs());<br /></pre><br />W przypadku standardowego (a'la JavaBean) mapowania uruchomienie poniższego kodu skutkuję następującą interakcją z BD: pobraniem <span style="font-style: italic;">A</span> z <span style="font-style: italic;">id=2</span>, pobraniem odpowiadającej kolekcji <span style="font-style: italic;">B</span>, usunięcie wszystkich <span style="font-style: italic;">B</span> przypisanych do danego <span style="font-style: italic;">A</span>, wstawienie kolejno poprzednio usuniętych <span style="font-style: italic;">B</span>.<br /></li><br /><li> oraz wykorzystanie immutable list przy mapowaniu:<br /><pre class="brush: java"> public class A{<br />@OneToMany<br />public List getBs(){<br />Collections.unmodifiableList(bs);<br />}<br />public void setBs(List b){<br />this.bs = bs;<br />}<br />}<br /></pre><br />Operacja załadowanie A po identyfikatorze ma skutkować następującą interakcją z BD: pobranie A, pobranie odpowiadających B, usunięcie odpowiadających B,dodanie kolejno usuniętych przed chwilą B.<br /></li><br /></ol>Dalej przedstawiono porównanie dostępnych strategii mapowania hierarchii. Szło to dość sprawnie i w końcu Patrycja przeszła do omówienia swojego projektu <a href="http://yonita.com/">Yonita</a>, który ma przeprowadzać analize kodu pod kątem potencjalnych defektów, <span style="font-style: italic;">anti-patterns, bad practices, vulnerabilities</span> i innych takich. Zgodnie z tym co można wyczytać na stronie projektu <span style="font-style: italic;">Yonita</span> dokonuję analizy statycznej jak i "dynamic web testing". Przy czym to drugie ma polegać na generowaniu testów automatycznych. Chyba jestem zbyt sceptycznie do tego nastawiony, ale parę lat temu rozmawiałem z goścmi z <a href="http://www.parasoft.com/">Parasoft</a> i chyba przestałem wierzyć w cudowne narzędzia, szczególnie jeśli chodzi o automatyczne generowanie testów.<br /><br />"Code Generation on the JVM" by Hamlet D'arcy. Kilka razy miałem przyjemność a raczej nie-przyjemność bycia na prezentacji, która omawiała różne narzędzia i metody związane z generacją kodu. Wszystkie one jednak bazowały na narzędziach do generowaniu kodu źródłowego(CORBA stub generation, WSDL2Java, Java Beans generation). Hamlet skoncentrował się na zupełnie innych narzędziach, a w szczególności na: <a href="http://www.blogger.com/projectlombok.org/">Lombok</a>, <a href="http://www.blogger.com/www.springsource.org/roo">Spring Roo</a> oraz paru innych wynalazkach do Groovy.<br />W jednym z projektów korzystam z Lombok i bardzo sobię to narzędzie chwalę. Jedyna rzecz która mnie bardzo irytuję to fakt, że pod eclipse (może pod innym IDE jest podobny problem) w przypadku dodawania przez eclipse (CTRL+1) nowej metody do klasy z annotacją <span style="font-style: italic;">Lombok</span> dostajemy w edytorze błąd. Wynika to z tego, że eclipse wstawił nam sygnaturę nowo utworzonej metody w miejsce, w którym są wygenerowane, "niewidzialne" metody. Nie licząc tej niedogodności, samo narzędzie jest na prawdę super. Nie obyło się bez nieśmiertelnego przykładu z generowaniem<span style="font-style: italic;"> getterów/setterów</span>, aby pokazać bardziej zaawansowane możliwości: <span style="font-style: italic;">@Cleanup</span> (w Java 7 będzie specjalna instrukcja związana z <span style="font-style: italic;">automatic resource management</span>), val ( <span style="font-style: italic;">final local variables</span> oraz detekcja typu), @Synchronized (enkapsulacja <span style="font-style: italic;">locków</span> wraz z generowaniem ich jako puste <span style="font-style: italic;">Object[]</span> by były serializowalne), <span style="font-style: italic;">@Log</span>. W celu sprawdzania jakie zmiany zostały dokonane przez <span style="font-style: italic;">Lomboka</span> można użyć <span style="font-style: italic;">javap</span> lub <a href="http://projectlombok.org/features/delombok.html"><span style="font-style: italic;">delombok</span></a>. Hamlet wspomniał o samym mechanizmie działania <span style="font-style: italic;">Lomboka</span>, który opiera się na annotacji, oraz <span style="font-style: italic;">Eclipse Handler</span> i <span style="font-style: italic;">Javac Handler</span>. Podobno stworzenie własnych rozszerzeń nie jest specjalnie trudne a jako <span style="font-style: italic;">first-step</span> warto zobaczyć <a href="http://www.ibm.com/developerworks/java/library/j-lombok/">tu</a>.<br />Następne na tapetę poszło <span style="font-style: italic;">Spring roo</span>, ale nie było już tak dokładnie omawiane jako <span style="font-style: italic;">Lombok</span>. Chyba Hamlet nie do końca zna ten projekt i skończyło się na odpaleniu jakiegoś bardzo prościutkiego przykładu oraz wspomnieniu o tym, że <span style="font-style: italic;">roo</span> działa w czasie kompilacji, korzysta z <span style="font-style: italic;">inter-type declarations</span> oraz jest dostępny mechanizm <span style="font-style: italic;">push-in refactoring.</span> Po przejściu projektów <span style="font-style: italic;">javowych</span>, Hamlet przeszedł do swojego świata <span style="font-style: italic;">Groovy</span>. Zaczęło się od <span style="font-style: italic;">Groovy</span> <span style="font-style: italic;">transformations</span> a już sama liczba dostępnych transformacji jest naprawdę imponująca. Dotyczą one: generowania kodu (np. @ToString, @Lazy (wraz z użyciem <span style="font-style: italic;">volatile</span> tworzy inteligentny <span style="font-style: italic;">lock</span> aby uniknąć dwukrotnego inicjalizowania), @TupleConstructor ), zarządzania współbieżnością (np. @Synchronized, @WithReadLock, @WithWriteLock), logowanie(np. @Log, @Slf4j) , ograniczaniem dostępu (np. @PackageScope), implementacji patternów (np. @Singleton, @Immutable, @Delegate) oraz wielu innych przypadków. Wszystkie z tych transformacji mają się opierać na implementacji interfejsu <a style="font-style: italic;" href="http://groovy.codehaus.org/api/org/codehaus/groovy/ast/GroovyCodeVisitor.html">GroovyCodeVisitor</a>, którego to metody są wykonywane przy przechodzeniu przez <span style="font-style: italic;">AST</span>. Następnie Hamlet pokazał projekt <a href="http://codenarc.sourceforge.net/">CodeNarc</a> do statycznej analizy kodu w <span style="font-style: italic;">groovy</span>, który też opiera się na wzorcu <span style="font-style: italic;">visitor</span>. Kolejny narzędziem był <a href="https://github.com/andresteingress/gcontracts/wiki/">GContracts</a>, który ma realizować idee <span style="font-style: italic;">desing by contract</span> dla <span style="font-style: italic;">groovy</span>. Pomimo tego, że projekt jest nowy, wygląda całkiem, całkiem. Ostatni był <a href="http://code.google.com/p/spock/">Spock</a>, który jest <span style="font-style: italic;">frameworkiem</span> do testowania napisanym w <span style="font-style: italic;">groovy</span>. Na pierwszy rzut oka sama idea przypomina użycie <a href="http://junit.sourceforge.net/javadoc/org/junit/runners/Parameterized.html">Parameterized runner</a>,w którym to dane podawane są w formacie <span style="font-style: italic;">wiki</span>. <span style="font-style: italic;">Spock</span> ma też podobno bardzo dobre wsparcie do <span style="font-style: italic;">mocków</span>.milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com1tag:blogger.com,1999:blog-9185236033651750735.post-49739192048448559112011-03-15T08:29:00.000-07:002011-03-16T07:14:58.225-07:00Testowanie wywołania JMSTemplateJakiś czas temu zostałem zapytany w jaki sposób przetestować jednostkowo coś takiego:<br /><pre class="brush: java"><br />public class SenderService {<br /> private JmsOperations jmsTemplate;<br /> <br /> public void doSend(final MyMessage myMessage){<br /> jmsTemplate.send(new MessageCreator() {<br /><br /> public Message createMessage(Session session) throws JMSException {<br /> return session.createObjectMessage(myMessage);<br /> }<br /> });<br /> }<br />}<br /></pre><br />Pytanie jest dość podchwytliwe, gdyż uważam, że testy jednostkowe bardzo dobrze się sprawdzają do testowania logiki systemu. Kod powyżej nie ma jakiejś extra rozbudowanej logiki, ale jest ona dość dobrze zakopana. Poza tym wychodząc od TDD (jak przykazane) jak mogło dojść, że mamy kod a teraz zastanawiamy się jak go przetestować ?<br />Tworzenie testów na tym etapie wydaję się sensowne jedynie w celach regresji.<br /><br />Z drugiej strony można się upierać, czy jest sens aby ten kod testować jednostkowo, czy nie testować go jedynie integracyjnie. Takie podejście wydaje się zgodne z tym co można wyczytać w <a href="http://www.growing-object-oriented-software.com/">http://www.growing-object-oriented-software.com/</a> a powyższy kod leżałby w warstwie <font style="font-style: italic;">adaptera</font> zgodnie z <a href="http://alistair.cockburn.us/Hexagonal+architecture">Ports and Adapters pattern</a>. Jednakże, z pragmatycznego punktu widzenia (albo wtedy gdy w ogóle nie mamy automatycznych testów integracyjnych/systemowych) przetestowanie jednostkowo powyższego kodu jest jak najbardziej możliwe.<br /><br />Najpierw zastanowiłbym się co chcemy przetestować, bo w metodzie <font style="font-style: italic;">send</font> dzieją się 2 rzeczy: wywołanie <font style="font-style: italic;">JMSTemplate.send</font> oraz pośrednio budowa <font style="font-style: italic;">JMS Message</font>. Najsensowniejsze wydaje się rozdzielenie obu funkcjonalności i przetestowanie ich osobno. Zacznijmy od testowania budowania <font style="font-style: italic;">JMS Message</font> za pomocą jednego z dwóch podejść:<br /><ol><li>tworzymy <font style="font-style: italic;">top level class</font> implementującą <font style="font-style: italic;">MessageCreator</font><br /><pre class="brush: java"><br />public class MyMessageCreator implements MessageCreator {<br /> private final MyMessage message;<br /><br /> public MyMessageCreator(MyMessage message) {<br /> this.message = message;<br /> }<br /> @Override<br /> public Message createMessage(Session session) throws JMSException {<br /> return session.createObjectMessage(message);<br /> }<br />}<br /></pre><br /></li><br /><li>tworzymy <font style="font-style: italic;">factory</font><pre class="brush: java"><br />MessageCreator getMessageCreator(final MyMessage message) {<br /> return new MessageCreator() {<br /> @Override<br /> public Message createMessage(Session session) throws JMSException {<br /> return session.createObjectMessage(message);<br /> }<br /> };<br /> }<br />}<br /></pre></li></ol><br /><br />Dzięki temu możemy w stosunkowo łatwy sposób przetestować, że nasz kod poprawnie tworzy<font style="font-style: italic;">JMS message</font> (na potrzebę testów użyjemy <font style="font-style: italic;">mocków</font>/<font style="font-style: italic;">stubów</font>)<br /><br />Następnie, możemy sprawdzić czy nasza metoda <font style="font-style: italic;">send</font> faktycznie użyje <font style="font-style: italic;">jmsTemplate.send</font> z odpowiednie parametrem. Taki test będzie zależał od tego w jaki sposób zaimplementowaliśmy tworzenie <font style="font-style: italic;">JMS Message</font>. Jeśli stworzyliśmy własną klasę <font style="font-style: italic;">MessageCreator</font> to możemy sprawdzić czy parametr metody send jest właśnie jej typu. Ewentualnie można użyć (gruba rura!) <a href="http://code.google.com/p/powermock/">PowerMock</a>. W drugim przypadku będziemy mogli <font style="font-style: italic;">zamockować factory</font>. Jeśli rolę fabryki pełni osobna klasa (potencjalnie ukryta pod interfejsem) to nie wydaję się to najgorsze (nie licząc tego że powstały nam kolejne artefakty), a w przypadku gdy fabryką będzie lokalna metoda to już wchodzimy w tematy <font style="font-style: italic;">partial-mocking</font>.<br /><br />Gdybyśmy jednak chcieli podany kod przetestować bez jego uprzedniej refaktoryzacji, czyli bez wprowadzania podziału na: budowania <font style="font-style: italic;">JMS message</font> i wywołanie <font style="font-style: italic;">jmsTemplate</font>, to też jest to jak najbardziej możliwe. Sprawa wydaje się stosunkowo prosta: należy sprawdzić czy metoda <font style="font-style: italic;">jmsTemplate.send</font> jest wywołana z odpowiednim parametrem. Odpowiedni parametr to taki, który jak wywołamy na nim metodę <font style="font-style: italic;">send</font><br />to na przekazanej <font style="font-style: italic;">JMS session</font> zostaną wywołane odpowiednie metody. Poniżej przykłady jak to zostało zrobione za pomocą <a href="http://easymock.org/">EasyMock</a> i <a href="http://mockito.org/">Mockito</a><pre class="brush: java"><br />public class SenderServiceEasyMockTest {<br /> private SenderService sut = new SenderService();<br /> private JmsOperations jmsOperations;<br /> private MyMessage myMessage = new MyMessage("neverMind");<br /><br /> @Before<br /> public void setUp() {<br /> jmsOperations = EasyMock.createMock(JmsOperations.class);<br /> sut.setJmsTemplate(jmsOperations);<br /> }<br /><br /> @Test<br /> public void testDoSendByArgumentMatcher() {<br /> final Session session = EasyMock.createMock(Session.class);<br /> IArgumentMatcher argumentMatcher = new IArgumentMatcher() {<br /><br /> public boolean matches(Object argument) {<br /><br /> MessageCreator messageCreator = (MessageCreator) argument;<br /> try {<br /> EasyMock.expect(session.createObjectMessage(myMessage))<br /> .andReturn(null);<br /> EasyMock.replay(session);<br /> messageCreator.createMessage(session);<br /> } catch (JMSException e) {<br /> throw new RuntimeException(e);<br /> }<br /> return true;<br /> }<br /><br /> public void appendTo(StringBuffer buffer) {<br /> }<br /> };<br /><br /> EasyMock.reportMatcher(argumentMatcher);<br /> jmsOperations.send((MessageCreator) null);<br /> EasyMock.replay(jmsOperations);<br /><br /> sut.doSend(myMessage);<br /><br /> EasyMock.verify(jmsOperations, session);<br /> }<br /><br /> @Test<br /> public void testDoSendByCapture() throws JMSException {<br /> Capture<Messagecreator> tocapture = new Capture<messagecreator>();<br /> jmsOperations.send((MessageCreator) EasyMock.and(EasyMock.anyObject(),<br /> EasyMock.capture(tocapture)));<br /> EasyMock.replay(jmsOperations);<br /><br /> sut.doSend(myMessage);<br /><br /> MessageCreator value = tocapture.getValue();<br /> Session session = EasyMock.createMock(Session.class);<br /> EasyMock.expect(session.createObjectMessage(myMessage)).andReturn(null);<br /> EasyMock.replay(session);<br /> value.createMessage(session);<br /> EasyMock.verify(jmsOperations, session);<br /> }<br />}<br /></pre><br /><br /><pre class="brush: java"><br />public class SenderServiceMockitoTest {<br /> private SenderService sut = new SenderService();<br /> private JmsOperations jmsOperations;<br /> private MyMessage myMessage = new MyMessage("SSSS");<br /><br /> @Before<br /> public void setUp() {<br /> jmsOperations = Mockito.mock(JmsOperations.class);<br /> sut.setJmsTemplate(jmsOperations);<br /> }<br /> <br /> @Test<br /> public void testDoSendByMatcher() { <br /> sut.doSend(myMessage);<br /> <br /> Mockito.verify(jmsOperations).send(Mockito.argThat(new ArgumentMatcher<Messagecreator>() {<br /><br /> @Override<br /> public boolean matches(Object argument) {<br /> MessageCreator messageCreator = (MessageCreator) argument; <br /> Session session = Mockito.mock(Session.class); <br /> try {<br /> messageCreator.createMessage(session); <br /> <br /> Mockito.verify(session).createObjectMessage(myMessage);<br /> } catch (JMSException e) {<br /> throw new RuntimeException(e);<br /> }<br /> return true;<br /> }<br /> }));<br /> }<br /><br /> <br /> @Test<br /> public void testDoSendByCaptor() throws JMSException { <br /> sut.doSend(myMessage);<br /> <br /> ArgumentCaptor<Messagecreator> messaArgumentCaptor = ArgumentCaptor.forClass(MessageCreator.class); <br /> Mockito.verify(jmsOperations).send(messaArgumentCaptor.capture()); <br /> Session session = Mockito.mock(Session.class); <br /> MessageCreator value = messaArgumentCaptor.getValue();<br /> value.createMessage(session); <br /> Mockito.verify(session).createObjectMessage(myMessage); <br /> }<br />}<br /></pre><br />Pomimo braku jakiegokolwiek refaktoringu testy w <font style="font-style: italic;">EasyMock</font> wyglądają o wiele mniej czytelniej od tych napisanych w <font style="font-style: italic;">Mockito</font>. Wynika to głównie z potrzeby wołania w odpowiednich miejscach <font style="font-style: italic;">EasyMock.replay</font> oraz (co gorsze) braku mechanizmów do weryfikacji zachowań, które zaszły w ramach wywołania metody podległej testowi. W przypadku <font style="font-style: italic;">EasyMock</font> jest potrzeba wyspecyfikowania <font style="font-style: italic;">a priori</font> wszystkich interakcji, a w <font style="font-style: italic;">Mockito</font> możemy wygodnie użyć <font style="font-style: italic;">Mockito.verify<font style="font-style: italic;"></font></font>. Szczególnie w tym przypadku bardzo wpływa to na czytelność kodu, gdy sekcje <font style="font-style: italic;">given/when/then</font> sa zaburzone. Dodatkowo użycie mechanizmu <font style="font-style: italic;">capture</font> w obu przypadkach wydaję się być zdecydowanie wygodniejsze od użycia <font style="font-style: italic;">argumentMatcher</font>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-25428238378882085422011-03-10T04:56:00.000-08:002011-05-06T02:57:05.859-07:00AspectJ a 2.2250738585072012e-308Ostatnio Dawid Weiss podczas spotkania w ramach Poznan JUG pokazał w jaki sposób obejść problem związany z <a href="http://www.exploringbinary.com/java-hangs-when-converting-2-2250738585072012e-308/">Double.parseDouble()</a>. Do czasu wydania (a co ważniejsze) zainstalowania <a href="http://www.oracle.com/technetwork/topics/security/alert-cve-2010-4476-305811.html">patcha</a> można się posiłkować wykorzystaniem AspectJ - nie pokryję to jednak wszystkich możliwych przypadków w których ten <span style="font-style: italic;">bug</span> może się objawić. Aspekt oryginalnie przedstawiony przez Dawida na prezentacji był - jak słusznie myślałem - niekompletny, bo m.in. nie pokrywał przypadku kiedy Double.parseDouble byłoby wołane po refleksji.<br /><pre class="brush: java"><br />package jug.demos.aspectj.aspects;<br />public aspect ParseDoubleHotFix{<br />double around(String s):<br />!within(jug.demos.aspectj.aspects..*) &&<br />call(double java.lang.Double.parseDouble(String)) &&<br />args(s){<br />if (s.indexOf("2250738585072012") >= 0) {<br />throw new IllegalArgumentException(<br />"We apologize for inconvenience, but this number is"<br />" temporarily not parseable by Oracle: " + s);<br />} else {<br />return Double.parseDouble(s);<br />}<br />}<br />}<br /></pre><br /><br />Pełny kod aspektu został przysłany dzień po prezentacji na listę Poznan JUG. Pierwsze co rzuciło się w oczy to fakt, że zdefiniowane <span style="font-style: italic;">pointcuty</span> są typu <span style="font-style: italic;">call</span> zamiast <span style="font-style: italic;">execute</span>. Czyli zamiast "opakować" wykonanie <span style="font-style: italic;">Double.parseDouble</span> za pomocą <span style="font-style: italic;">execute, </span> "opakowujemy" wywołania. Jak wyjaśnił Dawid jest to związane z tym "że java.lang.Double jest klasą systemową i ładuje się bardzo wcześnie. <span style="font-style: italic;">Load time weaver</span> w AspectJ nie byłby w stanie jej pewnie stosownie owinąć, nawet gdyby go zmusić. Domyślnie AspectJ nie przetwarza klas systemowych w ogóle (java.* i javax.*). To, co można ew. zrobić, to przetworzyć rt.jar w trybie offline (z wymuszeniem przetwarzania pakietów systemowych), ale nie próbowałem."<br /><br /><div>Próba obejścia tego błędu poprzez sprawdzenie wyłącznie własnego kodu jest zdecydowanie niewystarczające, ponieważ feralna liczba może być parsowana przez kod, który wykorzystujemy, a do którego nie mamy źródeł. Najgorzej jednak wygląda sprawa w przypadku gdy wywołanie byłoby z poziomu biblioteki standardowej java - wtedy rozwiązania z wykorzystaniem AspectJ nie za wiele by się zdało (chyba że offlinowe przetworzenie rt.jar o czym wspomniał Dawid). Jednak gdy odrzucimy ten skrajny przypadek to i tak zakres kodu który jest uruchamiany w ramach naszej aplikacji, a który bezpośrednio nie kontrolujemy daje duże pole do popisu wszelkiej maści <span style="font-style: italic;">vulnerabilities</span>. Jako przykład posłużył Dawidowi Tomcat 6.0.24. </div><div>Wydaję mi się, że eksperyment najłatwiej przeprowadzić przy wykorzystaniu AJDT wraz z podpiętym pod Eclipse Tomcatem (najłatwiej zrobić to w STS):<br /><ul><li>tworzymy sobie projekt typu <span style="font-style: italic;">AspectJ Project</span>.<br /></li><li>tworzymy w nowo otwartym projekcie nowy <span style="font-style: italic;">Aspect</span>,</li><li>uaktywniamy go poprzez utworzenie pliku z odpowiednimi wpisami w META-INF/aop-ajc.xml w projekcie utworzonym powyżej (dodatkowo dodamy sobie <weaver options=" -XnoInline -verbose -showWeaveInfo"/>)<br /></li><li>z poziomu IDE przechodzimy do konfiguracji Tomcata i otwieramy ustawienia związane z uruchomieniem serwera</li><li>w zakładce <span style="font-style: italic;">Arguments</span> do <span style="font-style: italic;">VM Arguments</span> dodajemy -javaagent:<ścieżka do aspectjweaver-1.6.10.jar></li><li>w zakładce <span style="font-style: italic;">Classpath</span> do <span style="font-style: italic;">User Entries</span> dodajemy utworzony powyżej projekt typu <span style="font-style: italic;">AspectJ</span></li><li>uruchamiamy Tomcata</li></ul>W logach zobaczymy m.in. wpis w stylu:<br /><pre class="brush: java"><br />weaveinfo Join point 'method-call(double java.lang.Double.parseDouble(java.lang.String))' in Type 'org.apache.catalina.connector.Request' (Request.java:2591) advised by around advice from<br /></pre><br />Oznacza to że w linii 2591 klasy <span style="font-style: italic;">org.apache.catalina.connector.Request</span> znajduję się wywołanie <span style="font-style: italic;">Double.parseDouble</span> (czyli w tej linii znajduje się <span style="font-style: italic;">jointpoint</span> spełniający określony <span style="font-style: italic;">pointcut</span> ). Przeglądając źródła klasy <span style="font-style: italic;">org.apache.catalina.connector.Request</span> można zobaczyć, że wywołanie <span style="font-style:italic;">Double.parseDouble</span> odbywa się przy w metodzie <span style="font-style: italic;">getLocale</span>, a dokładniej przy parsowaniu wartości <span style="font-style: italic;">quality factor</span>. Oznacza to, że w przypadku gdy na serwerze zostanie wykonana metoda <span style="font-style: italic;">request.getLocale</span> w odpowiedzi na żądanie HTTP o nagłówku <span style="font-style: italic;">Accept-Language</span> i jego dowolną wartością, ale z q=2-2250738585072012e-308, obsługujący wątek "trafi" w <span style="font-style: italic;">busy-loop</span>.<br /><br />Dawid w swojej prezentacji skupił się głównie na <span style="font-style: italic;">load-time weaving</span> - w odróżnieniu od <span style="font-style: italic;">compile/binary time weaving</span> takie podejście nie wymaga ingerencji w proces budowania kodu. Ma to znaczną zaletę, którą wykorzystałem podczas pracy u klienta, kiedy okazało się, że aplikacja działa zdecydowanie wolniej od tego co od niej oczekiwano. Zamiast próbować podłączać ją pod <span style="font-style: italic;">profiler</span> czy też optymalizować na ślepo, chciałem zmierzyć czas obsługi żadania z podziałem na warstwy/komponenty. Dodanie takiej logiki w kodzie byłoby nie tylko bardzo upierdliwe, ale także wymagałoby przebudowania i skompilowania aplikacji (klient wersjonował otrzymywane binaria). Zamiast tego napisałem aspekt, zapakowałem go w jara i jedyne co pozostało to zmuszenie administratora aby zmodyfikował parametry uruchamiania JVM (dodajemy <span style="font-style: italic;">javagent</span> oraz jar z aspektem do classpath). Nie potrzeba było w takim przypadku w żaden sposób przetwarzać dostarczonej poprzednio aplikacji. <br />Modyfikacja parametrów startowych JVM często trafia na opór ze strony administratorów, ale w tym przypadku szczęśliwie się udało. <br /><br />Z punktu widzenie developmentu/deploymentu <span style="font-style:italic;">LTW</span> jest strasznie wygodne - wystarczy stworzyć nowy <span style="font-style: italic;">AspectJ project</span>, napisać w nim aspekty, dodać ten projekt do classpath (w IDE bajecznie łatwe) projektu, dla którego chcielibyśmy aby zadziałały aspekty, zmodyfikować parametry startowe projektu (<span style="font-style: italic;">javaagent</span> ) i uruchomić....<br /><br />W <span style="font-style: italic;">SpringFramework AspectJ</span> jest w pełni wykorzystywany do implementacji <span style="font-style: italic;">@Configurable</span> oraz do zarządzania transakcjami w trybie <span style="font-style: italic;">aspect</span>. Uruchamianie testów za pomocą <span style="font-style: italic;">Spring TestContextFramework</span>, które tworzą <span style="font-style: italic;">context</span> korzystający z <span style="font-style: italic;">Load Time Weaving</span> dość znacząco zwiększa czas uruchomienia testów. Wynika to (zgodnie z tym co powiedział Dawid) z tego że sam <span style="font-style: italic;">weaver</span> jest napisany w javie (co implikuje problemy z opakowywaniem klas języka/biblioteki standardowej). Dodatkowo w kontekście wydajności:<br /><ul><li>unikanie <span style="font-style: italic;">dynamic pointcut</span> (typu <span style="font-style: italic;">cflow, cflowbelow</span>)</li><li>unikanie generycznych typów parametrów/wartości zwracanych</li><li>tworzenie <span style="font-style: italic;">pointcut</span> minimalizujących ilość <span style="font-style: italic;">jointpointów</span></li><li>sprawdzanie <span style="font-style: italic;">weaving logs</span></li></ul><br />Na końcu Dawid pokazał 2 dodatkowe zastosowania użycia <span style="font-style: italic;">AspectJ</span><br /><ul><li>mierzenie wydajności/śledzenie ścieżek wywołania<br /></li><li>szukanie błędów związanych z <span style="font-style: italic;">concurrency</span></li></ul>To drugie wygląda bardzo ciekawie, szczególnie, że błędy związane z <span style="font-style: italic;">concurrency </span>mogą się pojawiać losowo i są bardzo ciężkie do wykrycia za pomocą standardowych technik: <span style="font-style: italic;">unit testy/debugging</span>. Dawid zaproponował aby zdefiniować wymagania co do współbieżności wybranej klasy za pomocą aspektu - przy wejściu do metody podnosimy flagę (target + wątek), a przy wyjściu opuszczamy flagę (target + wątek) . Dzięki temu możemy sprawdzić czy przy wejściu do metody flaga jest opuszczona. Dawid zaproponował aby zrealizować to w taki sposób aby <span style="font-style: italic;">zdeployować</span> aspekt wraz z aplikacją na serwerze uruchomionym w trybie <span style="font-style: italic;">debug</span>. Dalej, podpiąć się do serwera z IDE, ustawić <span style="font-style: italic;">breakpoint</span> wraz z <span style="font-style: italic;">Suspend Policy</span> ustawioną na <span style="font-style: italic;">Suspend VM</span> w linii, która nie powinna zostać wywołana jeśli nasz kod który poddajemy sprawdzeniu jest poprawny. Maciej Biłas w mailu następnego dnia zaproponował aby zamiast bawienie się w tryb <span style="font-style: italic;">debug</span> i <span style="font-style: italic;">breakpointy</span> programowowo wykonać <span style="font-style: italic;">heapdump</span> i wczytać go do <span style="font-style: italic;">Eclipse Memory Analyzer</span>. <span style="font-style: italic;">HeapDump</span> wygenerowane przez jave w wersji od 6u14 mają zawierać extra informacje, która pozwolą <span style="font-style: italic;">Eclipse Memory Analyzer </span>na dogłębną analizę zawartości pamięci wraz z stanem wątków. Myślę że warto to sprawdzić... </div>. W kontekście <span style="font-style:italic;">debuggingu</span> warto sprawdzić projekt <a href="http://youdebug.kenai.com/">http://youdebug.kenai.com/</a>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-52238137178758795712010-05-22T06:15:00.001-07:002010-06-08T10:06:03.757-07:00GeeCon dzień 1<span style="font-size:130%;"><a href="http://2010.geecon.org/materials/presentations/2010_05_13/room_5/2010-geecon-fritzon.pdf">The Future of Java</a></span> - keynote w wykonaniu Thorbiörn Fritzona zdecydowanie nie rzucał na kolana. Chyba trochę zabrakło pomysłu na prezentacje. Thorbiörn rozpoczął od wspominania czasów gdy Sun był firmą zatrudniającą <span style="font-style: italic;">geeków (" We are geeks, we have no friends. We have peers and treat them as friends"), </span>którzy robili "cool stuff", ale nie mieli zbytnio głowy do robienia pieniędzy. Takie podejście (jak i sami ludzie, z których część odeszła z firmy) uległo zmianie po przejęciu firmy przez Oracle. Jednak samo przejęcie nie ma w zdecydowany sposób wpłynąć na los samej Javy. Większość produktów oferowanych przez Oracle (nie mówimy tu o bazie danych) jest oparta na Javie i dlatego powinno <span style="font-style: italic;">vendorowi </span>zależeć na tym aby język i sama platforma dalej się rozwijała. Zresztą skoro sama Java jest "open-source" to nawet gdyby Oracle miał upaść (co podobno mu w niedalekiej przyszłości nie grozi) to i tak sam język wraz z JDK pozostanie. Ogółem podobno nie ma się co martwić, a nawet można wyglądać w przyszłość z nadzieją: Oracle już teraz się angażuje (poprzez m.in. przekazywanie kodu) w działania na rzecz <span style="font-style: italic;">OS</span> oraz wyszedł z inicjatywą wprowadzenia zmian w JCP, ale w tej ostatniej sprawie żadnych dalsze szczegóły nie zostały podane...<br />Następnie Thorbiörn przedstawił parę cech nadchodzących w Java 7, napomknął o planowanym połączeniu grup tworzących JRockit i HotSpot, dalszym rozwoju NetBeans platform oraz GlassFish (wersja org i com).<br />Wspomniał dodatkowo o <a href="http://www.oracle.com/technology/products/jrockit/missioncontrol/index.html">JRockit Mission Control </a>oraz wchodzącym w jego skład JRockit Flight Recorder jako narzędziach do monitorowania i diagnozowania działania JVM. To ostanie narzędzie ma mieć możliwość nagrywanie co się dzieje w JVM (coś w stylu czarnych skrzynek w samolotach nagrywających parametry lotu) a następnie można wykonać <span style="font-style: italic;">dump</span> nagranych parametrów w zadanej przez nas chwili/interwale.<br /><br /><span class="lecture_title"></span><span style="font-size:130%;"><a href="http://2010.geecon.org/materials/presentations/2010_05_13/room_3/2010-geecon-herrmann.pdf">Object Teams: The Next Dimension of Modularity</a></span> - bardzo ciekawa prezentacja poruszająca temat związany z modułowością/modularnością. Temat ten w obecnej chwili kojarzy mi się od razu z zapowiadanymi zmianami w Java 7 lub też OSGI, jednak prezentacja dotykała trochę innego tematu. Zaczęło się od przypomnienia/objaśnienia czemu powinno zależeć nam na modularności: <span style="font-style: italic;">write/read/understand/change one piece at a time, clean reusability</span>. W tradycyjnym/książkowym podejściu OO system składa się z obiektów - bytów posiadających dane oraz zachowanie. Zachowanie systemu jest definiowane jako zbiór interakcja pomiędzy obiektami (każdy z obiektów dodaje swoją cegiełkę). Podejście to ma mieć kilka zasadniczych wad:<br /><ul><li><span style="font-style: italic;">no boundaries</span> - struktura taka nie pozwala nam w łatwy sposób określić, który obiekt z którym może się komunikować.</li><li><span style="font-style: italic;">bloated classes</span> - implementacja danego zachowania ma być realizowana przez wiele klas, a dodawanie kolejnych zachowań powoduję dodawanie kolejnych elementów do istniejących klas co powoduję, że nasze klasy są bardzo rozbudowane i mają wiele odpowiedzialności</li><li><span style="font-style: italic;">scattered behaviour</span> - skoro realizacja danego zachowania jest rozrzucona pomiędzy wieloma klasami można spodziewać się problemów przy <span style="font-style: italic;">maintaince: </span>tudności w zmienianiu/dodawaniu/usuwaniu danego zachowania</li></ul>Nie do końca zgadzam się z tymi argumentami. Wydaję mi się, że system napisany zgodnie z zasadami OO nie musi mieć tych (wszystkich) wad.<br />Drugie podejście opiera się na strukturze, w której to mamy do czynienia z <span style="font-style: italic;">operacjami</span> oraz <span style="font-style: italic;">encjami</span>. Operacje są bezstanowe i implementują logikę systemu a jednocześnie manipulują encjami (strukturami), które nie posiadają metod, a jedynie przechowują dane. Takie podejście też ma swoje wady<br /><ul><li><span style="font-style: italic;">no encapsulation for data</span> - encje trzymają tylko dane a nie posiadają metod, które działałyby na tych danych</li><li><span style="font-style: italic;">no “natural” structure within operation module</span> - zdecydowanie takie podejście jest bardzo mało obiektowe i wygląda mało naturalnie<br /></li><li><span style="font-style: italic;">no re-use between operations</span> - każda z operacji to osobny "świat"</li></ul>Podejście takie od razu przypomina mi <span style="font-style: italic;">Anaemic Domain Model</span> oraz <span style="font-style: italic;">Transaction Script</span> opisywane przez Martina Fowlera.<br />Wady obu podejść: <span style="font-style: italic;">Data Centric</span> i <span style="font-style: italic;">Behavoiur Centric</span> ma być pozbawiony <a href="http://www.eclipse.org/objectteams/">ObjectTeams</a>. Jest to projekt, w którym to dla naszych obiektów (tych obiektów trzymających dane oraz posiadających zachowania) nazywanymi od teraz "base" są tworzone struktury <span style="font-style: italic;">"role"</span>, a ich zarządzaniem zajmują się struktury <span style="font-style: italic;">"team"</span>. Dla danego <span style="font-style: italic;"><span style="font-style: italic;"></span>"Base"</span> może być zdefiniowane jedno lub więcej <span style="font-style: italic;">role</span>. Przypomina to trochę mechanizm dziedziczenia, ale jest to coś innego: dany base może występować nie tylko w wielu <span style="font-style: italic;">role </span>( a nawet w wielu <span style="font-style: italic;">role </span>tego samego typu), a poza tym <span style="font-style: italic;">role</span> są dynamiczne - w czasie działania dana instancja może występować w wielu <span style="font-style: italic;">role</span>. <span style="font-style: italic;">Team </span>implementują dane zachowanie (<span style="font-style: italic;">use-case</span>) poprzez enkapsulacje interakcji pomiędzy zbiorem obiektów <span style="font-style: italic;">role</span>. <span style="font-style: italic;"> Role</span> wzbogacają/zmieniają zachowanie odpowiadających im instancji <span style="font-style: italic;">base </span>poprzez mechanizm<span style="font-style: italic;"> call interception.</span> W ten sposób <span style="font-style: italic;">core behviour</span> znajduje się w <span style="font-style: italic;">base</span>, zachowania <span style="font-style: italic;">base </span>specyficzne dla danej funkcjonalności są zawarte w <span style="font-style: italic;">role</span>, które to są zdefiniowane w <span style="font-style: italic;">team</span>. Do tego dochodzi jeszcze mechanizm <span style="font-style: italic;">Team Activation</span>, ktory pozwala na drobno ziarniste włączanie/wyłączanie mechanizmu <span style="font-style: italic;">call interception.</span><br />Całość wygląda całkiem, całkiem. Pomimo tego, że <span style="font-style: italic;">role </span>są dynamiczne, eclipse w postaci <a href="http://wiki.eclipse.org/Object_Teams_Development_Tooling">Object Teams Development Tooling (OTDT)</a> ma zapewniać bardzo dobre wsparcie podczas developmentu, a jako dodatkowy plus ObjectTeams ma też działać pod Equinox.<br /><br /><span style="font-size:130%;"><a href="http://2010.geecon.org/materials/presentations/2010_05_13/room_5/2010-geecon-russel.pdf">Easy to Use Highly Available Java Database Access</a></span>- niestety nie byłem na całej prezentacji i przyszedłem po około 20 minut po rozpoczęciu. Craig przedstawiał <a href="http://www.mysql.com/products/database/cluster/">MySql Cluster</a> jako transakcyjną baza danych zaprojektowaną z myślą o pracy w silnie obciążonym środowisku wymagającym szybkiego, wydajnego i wysoce-dostępnego dostępu do danych. Jako przykład infrastruktury wskazano <span style="font-style: italic;">MySQL Cluster Carrier Grade Edition</span>, które to ma pracować z dostępnością "5 9's" wykonując miliony operacji na minute. Razem z długą listą firm, które to korzystają z takiego rozwiązanie oraz wybranymi scenariuszami użycia zrobiło to naprawdę duże wrażenie. Oczywiście osiągnięcie takich wyników niesie ze sobą pewne (a dla osób przyzwyczajonych do pracy z typowymi bazami SQL dość znaczne) <a href="http://dev.mysql.com/doc/mysql-cluster-excerpt/5.1/en/mysql-cluster-limitations.html">ograniczenia </a>co do mechanizmów przechowywania danych (na prezentacji Craig wspomniał jedynie o typach danych i braku ograniczenia <span style="font-style: italic;">foreign-key</span>). W dokumentacji projektu jasno jest napisane, że przejście z <span style="font-style: italic;">MyIsam </span>czy <span style="font-style: italic;">InnoDB </span>na <span style="font-style: italic;">NDB</span> (specyficzny dla MySQL Cluster <span style="font-style: italic;">storage engine</span> ) może wymagać zmiany schematu, zapytań albo nawet zmian w samej aplikacji.<br />Dostęp do <span style="font-style: italic;">MySql Cluster</span> można uzyskać za pomocą: C++ API, SQL ( za pomocą <span style="font-style: italic;">MySQL Server</span>) oraz <span style="font-style: italic;">MySQL Cluster Connector for Java.</span> Ten ostatni interfejs udostępnia 2 możliwości: <span style="font-style: italic;"> </span>, <span style="font-style: italic;">ClusterJ </span><span>lub </span><span style="font-style: italic;">ClusterJPA</span><span style="font-style: italic;"> </span><span>jako </span><span style="font-style: italic;">plug-in</span> dla <span style="font-style: italic;">OpenJPA</span>. <span style="font-style: italic;">ClusterJ </span>przypomina na pierwszy rzut oka JPA/JDO, też mamy do czynienia z mapowanie, ale trochę innym i zdecydowanie uboższym od tego, do którego jesteśmy przyzwyczajeni. Jest to związane z tym, że MyClusterJ nie jest <span style="font-style: italic;">out-of-box </span>bazą z interfejsem SQL - czyli mapowania nie przekładają się na SQL a na wewnętrzne API MySQL Cluster. Interfejs ClusterJ udostępnia nam dobrze znane obiekty: <span style="font-style: italic;">SessionFactory, Session, Transaction, Query</span>, ale mapowania robimy na interfejsie a nie na klasie. Dodatkowo nie są wspierane takie mechanizmy jak: relacje, dziedziczenia, tworzenie tabel/indexów na podstawie mapowania a zapytania zawsze dotyczą pojedynczej tabeli. ClusterJPA stanowi pewne rozszerzenie i obejście w/w ograniczeń (jestem ciekaw jaki podzbiór JPA jest tak naprawdę wspierany). Jest to plugin dla OpenJPA, który pewne operacje: <span style="font-style: italic;">primary key reads, inserts, updates, deletes</span> wykonuje przy pomocy ClusterJ API, a pozostałe realizuje za pomocą JDBC (czyli SQL). W przypadku innych <span style="font-style: italic;">providerów </span>JPA wszystkie operacje będą wykonywane za pomocą JDBC.<br /><br /><span class="lecture_title"></span><span style="font-size:130%;"><a href="http://2010.geecon.org/materials/presentations/2010_05_13/room_5/2010-geecon-ciurana.pdf">The High Availability Non-Stop, Fault-Tolerant Services Tutorial</a></span><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title"></span></span><span style="font-size:180%;"> </span>- całkiem ciekawa prezentacja dotyczącą tworzenia skalowalnych i "niezatapialnych" systemów.<br />Eugene rozpoczął od samych podstaw. Na początku omówił czym jest skalowalność (własność systemu umożliwiająca obsługę rosnącej ilości pracy lub też łatwość rozbudowy istniejącego systemu w przypadku zwiększenia zapotrzebowania na pewne zasoby: sieć, moc obliczeniowa, dostęp do BD), typy skalowalności: <span style="font-style: italic;">horizonal-out</span> ( dodanie nowych węzłów mających taką samą funkcjonalność jak te dotychczas), <span style="font-style: italic;">vertical-up</span> (rozbudowa istniejących węzłów poprzez dodanie zasobów, np. pamięci, procesorów, <span style="font-style: italic;">storage</span>). Dalej zdefiniował <span style="font-style: italic;">high availability</span> (w zadanym okresie czasu zapewnienie całkowitej ciągłość działania systemu pod kątem funkcjonalnym ) i podał sposób obliczania dostępności jako 100 - (100*D/U) gdzie D to <span style="font-style: italic;">unplanned downtime</span> a U to <span style="font-style: italic;">uptime</span>. Szczególnie podkreślił fakt, że <span style="font-style: italic;">uptime != available</span> (system może być uruchomiony, ale ze względu na np. problemy z siecią jest niedostępny). W ten sposób przeszedł do tabelki definiującej poziom dostępności jako ilość 9 - w zależności od ilości 9 system nie będzie <span style="font-style: italic;">up</span> przez określony czas w roku. Określanie dostępności w taki sposób ma wiele niedoskonałości i ma służyć głównie w materiałach marketingowych. Problemem samy w sobie jest <span style="font-style: italic;">uptime</span> (system jest <span style="font-style: italic;">up, </span>ale ze względu na awarie sieci jest niedostępny dla klientów), poza tym z powodów problemów wydajnościowych system może być uznany za <span style="font-style: italic;">up</span>, ale praktycznie nieużyteczny dla użytkowników, gruboziarniste uśrednianie poziomu dostępności nie odzwierciedla wymagań wielu systemów (brak dostępności systemu w okresie <span style="font-style: italic;">peak load</span> a niedostępność w okresie "wakacji" ). Definicja <span style="font-style: italic;">HA </span>oraz sposobu mierzenia <span style="font-style: italic;">uptime </span>powinny zostać jasno sprecyzowane w <span style="font-style: italic;">SLA </span>(<span style="font-style: italic;">Service level agreement</span>). Po tym teoretycznym wstępie (choć nie wiem czy go do końca poprawnie zrozumiałem) przeszedł do bardziej praktycznych zagadnień:<br /><ul><li><span style="font-style: italic;">load balancer</span> - "rozrzuca" żądania pomiędzy 2 lub więcej zasobami, ukrywając je jednocześnie przed klientami, którzy widzą jedynie pojedynczy zasób w postaci <span style="font-style: italic;">load balancera</span>. Mogą być bezstanowe (typowe dla żądań webservices) lub stanowe (np. dla systemów wymagających współdzielenia sesji HTTP). Istnieje wiele możliwych algorytmów "rozrzucania" żądań do zasobów, np. na podstawie contentu, priorytetów, mocy obliczeniowej zasobów, itd.</li><li>cache - przechowywanie danych które są "ciężkie" do pobrania lub obliczenia. Cache sprawdza się szczególnie dobrze dla danych <span style="font-style: italic;">read-only</span>. Problemem niestety są wszelkie bezpośrednie zapisy do danych trzymanych w cache. Standardowo można to rozwiązać przez <span style="font-style: italic;">write-through</span> - synchronicznie uaktualniamy cache oraz <span style="font-style: italic;">storage</span>, write-behind - wartość w <span style="font-style: italic;">cache </span>oznaczamy jako <span style="font-style: italic;">dirty</span> i zapisujemy do <span style="font-style: italic;">storage </span>asynchronicznie, no-write allocation - zakładamy, że z cache tylko czytamy i nie ma zapisów. Podane strategie chyba mają dotyczyć tylko przypadków w których to cache pełni role "proxy" dostępu do faktycznego <span style="font-style: italic;">data source</span>. Osobiście nasunął mi się przypadek w którym do <span style="font-style: italic;">data source</span> jest modyfikowany "z boku" przez inna aplikacje która jest nieświadoma cache. Odwołania do cache mogą być realizowane w sposób nie jawny (,np. Terracota, która też posiada API do cachowania ) lub jako bezpośrednie (,np. Coherence, Memcache). Wspomniał także o <span style="font-style: italic;">web caching</span> i wyróżnił 2 podejścia: <span style="font-style: italic;">web accelerators</span> ( użycie CDN jak S3, Akami , które to będą serwować zasoby) lub <span style="font-style: italic;">proxy cache </span>(Squid)<br /></li><li><span style="font-style: italic;">cluster </span>- grupa 2 lub więcej maszyn połączonych szybką siecią. Klaster jest widoczny dla użytkownika jako pojedyncza maszyna i jest budowany w celu zwiększania dostępności/wydajności systemu. Przy założeniu tej samej wydajności budowa klastra jest bardziej opłacalna niż skalowania pojedynczej maszyny (<span style="font-style: italic;">scale up</span>). Wyróżniamy konfiguracje A/A (active/active) w której to wszystkie maszyny jednocześnie pracują oraz A/P (active/passive) w której to część maszyn rozpoczyna prace tylko gdy inne ulegną awarii - wymagane są co najmniej 2 węzły (główny i zapasowy), mechanizm wykrywania awarii oraz możliwość przełączenia ruchu do węzłów zapasowych w momencie awarii.</li><li><span style="font-style: italic;">grid </span>- system, realizujący dane zadanie jako zbiór niezależnych od siebie <span style="font-style: italic;">tasków</span> (<span style="font-style: italic;">Map-Reduce</span>)</li></ul>Następnie Eugene poruszył tematy związane z<span style="font-style: italic;"> redundancy</span><span style="font-style: italic;"> i fault-tolerance</span>. W konfiguracji A/A w przypadku gdy nasza aplikacja jest bezstanowa sytuacja wydaje się bardzo prosta - jeden węzeł wylatuje i można działać dalej. W przypadku konfiguracji A/P (na której mają być uruchomione aplikacje "stanowe" które mają skalować się jedynie "w górę"- scale up) obługa fault-tolerance jest trudniejsza i droższa: wymagane jest przełączenia na zapasowe węzły i może wymagać ingerencji ze strony administratora. W dalszej części przedstawione zostały przykładowe konfiguracje i schematy architektur które mają zapewniać <span style="font-style: italic;">scalability </span>i <span style="font-style: italic;">HA</span>. Eugene chyba nie do końca wstrzelił się z czasem bo konfiguracje były dość rozbudowane i przybliżenie jakichkolwiek szczegółów trwałoby zdecydowanie za długo. Wspomniany został case w którym to pewna firma miała coraz to większe ilość danych do przetwarzania, ładowanie ich do bazy Oracle stawało się coraz bardziej bezsensowne: rosły koszty rozbudowy bazy a czas przetwarzanie rósł. Rozwiązaniem okazał się Hadoop. W innym przypadku dzięki zastosowaniu odpowiedniej architektury (w szczególności <span style="font-style: italic;">load-balancerów </span>na wielu poziomach) bardzo łatwo można było przeprowadzać inkrementalne uaktualnienie systemu.<br />Ogółem prezentacja bardzo ciekawa, ale ze względu na czas przedstawiona trochę po wariacku.<br /><br /><span class="lecture_title"></span><span style="font-size:130%;"><a href="http://2010.geecon.org/materials/presentations/2010_05_13/room_3/2010-geecon-weiss-linear.pdf">Java in high-performance computing</a></span> - chyba najciekawsza prezentacja dnia. Dawid po chwili mniej-czy-bardziej rozrywkowej przeszedł do konkretów: czy Java jest szybsza od C++?<br />Odpowiedz jest godna konsultanta: to zależy ... Dawid rozpoczął od zdefiniowania HPC (<span style="font-style: italic;">High-performance computing</span>) na potrzeby swojej prezentacji: działamy w środowisku o ograniczonych zasobach (np. CPU, pamięć) i mamy ograniczony czas na wykonanie przetwarzania. System napisany w duchu HPC nie powinien mieć żadnych oczywistych wad. Dawid przedstawił kolejne przykłady, które ukazywały znaczne różnice w czasie wykonania w zależności od pewnych warunków/ustawień:<br /><ul><li>funkcja dodająca 2 argumenty ma zdefiniowane odpowiednio argumenty: 2 <span style="font-style: italic;">inty</span> (prymitywy), 2 <span style="font-style: italic;">Integer</span>y, <span style="font-style: italic;">Integer </span><em>vararg. </em>Różnice w czasie wykonania w zależności od maszyny wirtualnej potrafią wynieść nawet kilkanaście sekund.</li><li>uruchomienie identycznej aplikacji na windows oraz ubuntu kończy się zupełnie inaczej pomimo tego, że są to te same JVM. Na ubuntu JVM uruchamiał aplikacje automatycznie w trybie "<i>server VM"</i></li></ul>Ze względu na to że mamy wielu dostawców JVM, istnieje wiele różnych wersji tych maszyn, mogą mieć włączone różne opcje, różne kompilatory (<span style="font-style: italic;">server</span>/<span style="font-style: italic;">client</span>) nie ma uniwersalnej metody tworzenia wydajnych aplikacji. Dodatkowo, to co faktycznie zostanie uruchomione nie jest do końca określone w <span style="font-style: italic;">bytecode</span>. Jedyną drogą osiągnięcia wydajności wydaje się być cykl: zmierz-popraw-zmierz.<br />Jeśli chodzi o benchmarking to Dawid znów pokazał parę solidnych przykładów:<br /><ul><li>czas wykonania martwego kodu może się różnić w zależności od JVM. <span style="font-style: italic;">HotSpot </span>bardzo ładnie sobie radzi z usuwaniem martwego kodu, zostało to szczególnie ciekawie pokazane za pomocą odpowiedniej flagi dla JVM, dzięki której mogliśmy zobaczyć kod w assemblerze.</li><li><span style="font-style: italic;">Hostpot </span>bardzo dobrze sobie radzi z wywołaniami metod wirtualnych: potrafi przeprowadzić optymalizację (<span style="font-style: italic;">aggressive inlining</span>), ale w odpowiednim momencie może się z tego wycofać i powrócić do standardowego wywołania wirtualnego.</li><li>wywołanie metody ze standardowego API javy (Integer.bitCount()) zostało zamienione przez <span style="font-style: italic;">JIT </span>na odpowiednią instrukcje procesora, która to była dostępna na maszynie na której to aplikacja była uruchomiona.<br /></li></ul>Mierzenie wydajności wcale nie jest trywialne a wartości, które otrzymamy mogą zależeć od wielu czynników. Sam proces mierzenia powinien odbywać się w warunkach stabilnych: po fazie "warm-up", odpalamy odpowiednią ilość przebiegów, wyciągamy średnie, wariancje, min, max itd. Ważne aby nasze scenariusze testów odpowiadały faktycznym scenariuszom użycia systemu i aby mierzyć na środowisku docelowym, ponieważ jak się okazało ma to bardzo duże znaczenie...<br />Więcej na ten temat można znaleźć w <a href="http://www.ibm.com/developerworks/library/j-jtp12214/">artykule</a>.<br />Pewne flagi wraz z objaśnieniami których używał Dawid można znaleźć <a href="http://blog.headius.com/2009/01/my-favorite-hotspot-jvm-flags.html">tu</a><br /><br /><span style="font-size:130%;"><span class="lecture_title"></span><a href="http://2010.geecon.org/materials/presentations/2010_05_13/room_5/2010-geecon-aniszczyk.pdf">An Introduction to EclipseRT, Equinox and OSGi</a></span> - nigdy nie byłem fanem prezentacji o <span style="font-style: italic;">eclipse</span>. Uważam że jest to (bardzo) dobre narzędzie i tak na prawdę tylko narzędzie. Zdarzało mi się widzieć prezentacje o Eclipse RCP, Eclipse RAP czy Eclipse Platform, ale nie do końca byłem przekonany co do użyteczności tych narzędzi w tworzeniu rozwiązań biznesowych. Byłem w stanie wyobrazić sobie ich wykorzystanie do tworzenia różnego typu narzędzi lub "rozwiązań naukowych". Jak się później okazało, większa część środowiska myśli bardzo podobnie do mnie.<br />Chris rozpoczął od krótkiego objaśnienia <span style="font-style: italic;">OSGI</span>: moduły jako <span style="font-style: italic;">bundle</span>, <span style="font-style: italic;">import/export package</span>, dynamiczne <span style="font-style: italic;">services</span>, <span style="font-style: italic;">BundleContext</span>, <span style="font-style: italic;">BundleActivator </span>. Nie obyło się bez wspomnienia o korzeniach <span style="font-style: italic;">OSGI</span>, <span style="font-style: italic;">OSGI Alliance</span>, <span style="font-style: italic;">OSGI Specifications</span> oraz implementacjach OSGI (np. Equinox, Fexlix, Knopflerfish, Concierge). Następnie Chris przedstawił <a href="http://www.eclipse.org/eclipsert/">EclipseRT </a>jako zbiór narzędzi, <span style="font-style: italic;">frameworków</span> oraz<span style="font-style: italic;"> </span>środowisk uruchomieniowych (runtimes) do wykorzystania przy budowie własnych rozwiązań, niezależnie od tego czy są to systemy typu <span style="font-style: italic;">desktop</span>, <span style="font-style: italic;">web</span>, <span style="font-style: italic;">SOA</span>, <span style="font-style: italic;">enterprise, embedded</span>. Na liście projektów wchodzących w skład EclipseRT można znaleźć wielu "starych znajomych" m.in: <span style="font-style: italic;">EclipseLink, Jetty, Birt, Swordfish, RAP</span>,<span style="font-style: italic;"> Virgo (Spring DM)</span>. Pomimo, że każdy z nich dotyczy zupełnie innej dziedziny to łączy je fakt uruchamiania na platformie <span style="font-style: italic;">Equinox </span>(też wchodzi w skład EclipseRT). Dzięki temu, że pod spodem mamy OSGI, projekt oraz nasze własne aplikacje/moduły mogą korzystać z wszystkich dobrodziejstw związanych z tym środowiskiem. Jako przykład wykorzystanie EclipseRT został przedstawiony <a href="http://wiki.eclipse.org/Toast">Toast</a>. Jest to system do zarządzania flotą pojazdów, oparty na EclipseRT, który ma służyć jako działający przykład rozwiązania wykorzystującego mechanizmy i możliwości EclipseRT.<br />W dalszej części Chris wspomniał o <span style="font-style: italic;">stackless stack</span> oraz o <a href="http://www.eclipse.org/eclipsert/whitepaper/20080310_equinox.php">CODA</a> - architekturze budowania systemów opartej o komponenty. Pomysł sam w sobie nie jest niczym nowym (już <em>JavaBeans </em>opisywało tworzenie aplikacji z komponentów), ale tym razem dzięki OSGI wszystko zapowiada się trochę inaczej.milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-53326882008471001362010-03-26T13:05:00.000-07:002010-03-31T10:50:28.253-07:004developersDziś miałem okazje wziąć udział w konferencji 4developers. Udział w konferencji zawdzięczam mojemu ukochanemu Poznan JUG.<br /><br /><span style="font-weight: bold;">Walka o życie – kuzyni i następcy Javy opanowują świat Michael Hunger</span> prezentacja była tak nudna,że po 10 minutach wyszedłem i przeniosłem się do sali ze ścieżki PHP. Pomimo tego że nigdy nie napisałem linijki w PHP(z czego tak naprawdę jestem dumny) to temat <span style="font-style: italic;">cloud computingu</span> przemawiał do mnie jak najbardziej.<br /><br /><span style="font-weight: bold;">PHP w chmurze Ivo Jansch</span> prezentacja okazała się bardzo ciekawa i nie miała za wiele wspólnego z PHP. Na początku omówione zostały podstawowe pojęcia związane z <span style="font-style: italic;">cloud computing</span>, a w szczególności: <span style="font-style: italic;">IaaS, PaaS, SaaS</span>. Jeśli chodzi o <span style="font-style: italic;">IaaS</span> to jest to podstawowy poziom, który z punktu widzenia klienta opiera się na tym że uzyskujemy dostęp do hardware (sami definiujemy wymagania co do tego hardware )na którym możemy zainstalować system operacyjny, a następnie samą aplikacje. To gdzie fizycznie zostanie uruchomiona aplikacja najczęściej nas nie interesuje...<br />Instalacja systemu operacyjnego jest zazwyczaj pomijana ze względu na to, że najczęściej wybiera się obraz maszyny wirtualnej z zainstalowanych już systemem operacyjnym. Jako developer dostajemy dostęp do takiego "obrazu" i możemy sobie instalować naszą aplikacje. Ivo szczególnie polecał zaznajomienie się z <a href="http://www.rackspacecloud.com/">rackspacecloud</a>, który ma dostarczać bardzo wygodne API, dzięki któremu z poziomu PHP można odpalić nową maszyną wirtualną i zdeployować aplikacje. Kolejny poziom to PaaS, czyli w tym przypadku przechodzimy o poziom wyżej - z poziomu systemu operacyjnego na którym możemy sobie odpalić dowolną aplikacje, otrzymujemy platformę (w pewnym sensie możemy mówić o serwerze-usłudze) na którym odpalamy naszą aplikacje. Najlepszym przykładem jest tutaj Google App Engine który pozwoli nam zdalnie <span style="font-style: italic;">deployować</span> aplikacje napisane w Java i Python. Jak na razie nie ma dostępnej darmowej infrastruktury PaaS dla PHP (istnieje płatne rackspacecloud sites). Ostatni poziom to SaaS czyli gdy tak na prawdę sama aplikacja jest usługą, z której to możemy korzystać. Najlepszym przykładem to Google Apps (docs, spreadsheet) lub też SalesForce CRM. Następnie Ivo omówił typowe problemy (choć lepiej nazwać to specyfiką środowiska) związane z tworzeniem aplikacji uruchamianych w chmurach: bezpieczeństwo, obsługa stanu, regulacje prawne. Na końcu wspomniał jeszcze o <span style="font-style: italic;">private developer clouds</span>(jako VMware vSphere oraz tych udostępnianych przez <a href="http://www.terremark.com/default.aspx">terremark</a>).<br /><span style="font-weight: bold;">Automatyczne generowanie kodu Marek Berkan</span> prezentacja sponsora, która niestety okazała się bardzo słaba. Mimo, że nie spodziewałem się cudów, to jednak liczyłem na coś więcej ... Przedstawione zostało parę scenariuszy, w których to generowanie kodu ma być bardzo użyteczne: generowanie kodu encji oraz DAO na podstawie schematu bazy danych, generowanie obiektów Javy z XML. Wszystko to było mało odkrywcze: użycie Hibernate, JAXB. Patrząc na Hibernate z punktu widzenia "generacji kodu" to wyniki nie są oszałamiające: głowną bolączką ma być brak automatycznego generowania stałych definiujących nazwy kolumn. W czasie prezentacji pokazał 2 pomysły, które wydają się bardzo sensowne: generowanie stałych dla propertiesów <span style="font-style: italic;">Strutsowych form beanów</span> oraz dla kluczy bundli. Dzięki temu wszelkie zmiany w <span style="font-style: italic;">źródle</span> (definicji form beana czy plikach bundli) są wyłapywane przez kompilator.<br /><span style="font-weight: bold;">TopLink Grid – skalowanie aplikacji korzystających z Java Persistence API Waldekmar Kot</span> - jedna z najlepszych (przynajmniej dla mnie ) prezentacji. Waldek zaczął trochę ospale od przedstawienia standardu <span style="font-style: italic;">JPA</span> i podstawowych pojęć z nim związanych: <span style="font-style: italic;">EntityManager, EntityManagerFactory, PesistenceUnit</span> i czegoś tam jeszcze. Ze względu na to że używam <span style="font-style: italic;">Hibernate</span> nie do końca rozumiem te wszystkie pojęcia...<br />Następnie omówił standarodowe mechanizmy <span style="font-style: italic;">cachowania</span>: cache transakcyjny(patrząc z perspektywy Hibernate cache związany z <span style="font-style: italic;">Session</span>), oraz ten drugi (nie pamiętam dokładnie jakiego Waldek użył sformułowanie, ale chodziło o cache na poziomie <i>SessionFactory </i>czyli, s<i>econd-level cache</i>). Ten ostatni jest współdzielony przez cache transakcyjne w ramach pojedynczego JVM, ale może być także współdzielony w ramach wielu JVM. W ten to sposób doszedł do sedna, czyli pojęcia <i>Data Grid</i>: jako osobnej warstwy (najczęściej osadzonej na innym JVM/innej maszynie fizycznej niż nasza aplikacja ) która świadczy usługi przechowywania ale także może przetwarzać obiekty. Zazwyczaj taki Data Grid składa się z większej liczby węzłów, które to tworzą ze sobą sieć P<i>eer-to-Peer</i> i dostarczają nam możliwość bezpiecznego/skalowalnego/wydajnego przechowywania w pamięci dużej ilości danych. Bardzo podobała mi się mechanizm związany z możliwością wykonywania operacji bezpośrednio na <i>Data Grid</i>: zamiast ściągać dane do aplikacji, modyfikować je i odsyłać, można "wysłać" operacje bezpośrednio w <i>Data Grid</i>. Połączenie ze sobą <i>Data Grid</i> (w przypadku Oracle mówimy tu o konkretnym produkcie Oracle Coherence) wraz z implementacją JPA (Oracle Toplink) daję nam <i>Oracle TopLink Grid</i>. Takie połączenie pozwala na zwiększenie wydajności i skalowanie naszej warstwy DAO poprzez sam mechanizm <i>cachowania</i>, ale jednocześnie dostarcza kilku nowych możliwości. Chodzi o to że Oracle Coherence pracujący tuż za plecami naszej warstwy DAO (ale przed bazą danych) może pracować w jednym z 3 trybów:<div><ul><li>Cache: standardowy <i>second-level cache</i> (cachowanie na podstawie id)</li><li>Read: w stosunku do trybu powyżej, nasze zapytania SQL są "przechywytywane" przez Data Grid, interpretowane oraz pobierane bezpośrednio z cache. Zapytania mają być rozpraszane na poszczególne instancje składające się na Data Grid (osobiście jestem ciekaw jak wygląda to w przypadku zapytań agregujących które przechodzą wzdłuż przez dane fizycznie przechowywane na innych instancjach Data Grid)</li><li>Entity: dodatkowo w przypadku modyfikacji najpierw modyfikowane są obiekty w Data Grid a później dane w bazie danych. Jednocześnie jest możliwe takie ustawienie że dane do modyfikacji są buforowane (ze względu na ilość lub czas) i wysyłane w 1 paczce do bazy danych.</li></ul><div>Temat na pewno był bardzo ciekawy, ale ze względu na ograniczenia czasowe (dobrze, ze po tej prezentacji był obiad- a tylko szczęśliwcy dostali voucher :( , można było jeszcze trochę Waldka dopytać) nie udało się pokazać więcej.</div><div style="font-weight: bold;"><br /></div></div><br /><span style="font-weight: bold;">Zbuduj pierwszą aplikację typu RIA z wykorzystaniem Flex 4 Piotr Walczyszyn<br /></span> - bardzo fajna prezentacja, której głównym punktem była prezentacja <a href="http://www.thesun.co.uk/sol/homepage/desktopkeeley/article1377719.ece">Desktop Keeley</a> :) Prezentacja składała się z 2 części: teoretycznej, w której omówione zostały podstawowe informacje na temat platformy FLEX, oraz praktycznej, w której to Piotr przedstawił w jaki sposób tworzyć w niej aplikacje. W części pierwszej można było usłyszeć o Flex SDK, sposobach komunikacji pomiędzy modułami SWF a częścią serwerową (SOAP, HTTP/HTTPS, AMF/AMFS, RTMP/RTMPS). Nie mogło się obyć bez prezentacji przykładowych aplikacji wykorzystujących FLEX: <a href="http://www.windowshop.com/">http://www.windowshop.com/</a>, <a href="http://www.taaz.com/makeover.html">http://www.taaz.com/makeover.html</a>, oraz "popisowej" <a href="http://www.adobe.com/devnet/flex/tourdeflex/">http://www.adobe.com/devnet/flex/tourdeflex/</a>. Następnie pojawiło trochę informacji na temat Adobe AIR, czyli <span style="font-style: italic;">runtime</span> umożliwiający uruchamianie aplikacji <span style="font-style: italic;">Flex </span>na <span style="font-style: italic;">desktop</span>. Dzięki temu że można wyjść poza <span style="font-style: italic;">sandbox</span> przeglądarki można skorzystać z dodatkowej funkcjonalności: instalacja i uruchamianie jako natywna aplikacja, dostęp do systemu plików, dostęp do wbudowanej bazy danych SQLite, możliwość uruchamianie aplikacji na wielu platformach: Windows, Mac, Linux a także Nokia S60 i Android...<br />"Lokalnym" przykładem aplikacji Adobe AIR to <a href="http://e-deklaracje.mf.gov.pl/">e-deklaracje</a>, a inne ciekawe można zobaczyć na <a href="http://www.adobe.com/pl/products/air/showcase/">stronach Adobe</a>. W 2 części Piotr przedstawił w jaki sposób stworzyć prostą aplikacje we Flex 4: aplikacja stworzona we FLEX łączy się z serwerem - Apache Tomcat, BlazeDS, MySQL. Musze przyznać, że robiło to duże wrażenie, szczególnie jeśli chodzi o skrócenie czas developmentu - choć nie do końca wiem czy to zasługa FLEX4 czy Flex Builer. Szczególnie spodobał mi się w części klienckiej mechanizm automatycznego generowania <span style="font-style: italic;">proxy</span> oraz <span style="font-style: italic;">dto</span> dla wystawionych na serwerze usług. Co ciekawe, zaimplementowany został (bez pokazania szczegółów) mechanizm asynchronicznego powiadamiania klientów. Miał to działać na takiej zasadzie, że usługa dodająca element do bazy wysyłała wiadomość do <span style="font-style: italic;">JMS topic</span>. Na tym <span style="font-style: italic;">topicu</span> "słucha" BlazeDS i powiadamia klientów za pomocą HTTP.<br />Możliwość asynchronicznego powiadamiania klientów (<span style="font-style: italic;">server push</span>) bez potrzeby korzystania z warswty pośredniej - JMS, ma być zaimplementowana w komercyjnym Flash Media Server lub open-source red5.<br /><br /><span style="font-weight: bold;">Co nowego w Java SE 7? Marcin Kalas</span> hmm nie do końca chyba słuchałem, a przynajmniej nic nie zanotowałem... chyba przychodzą na tą prezentacje miałem złe nastawienie: no bo przecież mój ukochany Websphere 6.1 działa na Java 5... Z tego co pamiętam to zostało wspomniane o paru udogodnieniach:<br />nowe File I/O, udogodnienia związane z przetwarzaniem równoległym JSR 166, nowy <span style="font-style: italic;">garbage collector</span>, skompresowany wskaźnik 64 bitowy. Myślę że wszystko co było powiedziane można znaleźć <a href="http://openjdk.java.net/projects/jdk7/features/">tu</a><br /><br /><span style="font-weight: bold;">Craftsmanship i wzorce projektowe Sławek Sobótka</span> - prezentacja zdecydowanie mniej techniczna od innych, ale całkiem ciekawa. Sławek skupił się na przedstawieniu wzorców projektowych jako ważnego ogniwa dojścia do profesjonalizmu. Wzorce nie tylko wypada znać, rozumieć, ale też wiedzieć kiedy stosować. Ważne jest aby pamiętać, że zastosowanie każdego z wzorców niesie ze sobą określone korzyści ale też i koszty. Mi osobiście spodobało się użycie <span style="font-style: italic;">chain of responsibility</span> do wybierania strategii oraz wzbogacanie strategii poprzez <span style="font-style: italic;">decorator</span>.<br /><br /><span style="font-weight: bold;">Modele komponentowe SCA, OSGi, Distributed OSGi i OSGi Enterprise a Java EE Jacek Laskowski</span> - dziwna prezentacja, chyba nie do końca wiem do czego Jacek dążył ... Rozpoczęło się od próby zdefiniowania podstaw: interfejs, usługa, komponent poprzez bardziej abstrakcyjne byty: "rusztowanie" i "szkielet", ale później poszło już z górki. Jacek przedstawił historie powstawania "architektur komponentowych" na platformie Java. Zaczęło się od JavaBeans, przez EJB, Spring, JEE, OSGi, Spring DM, OSGi Blueprint Container, Distributed OSGi, Enterprise OSGI, SCA. Ja wyłączyłem się przy SCA i tego przejścia nie zaczaiłem...<br />Prezentacja miała omawiać aspekty programowania deklaratywnego, ale tak na prawdę przemieniła się (przynajmniej ja to tak odebrałem) jako refleksje na temat architektury komponentowej na platformie. Jacek (w mojej ocenie) popełnił 2 błędy/nieścisłości:<br /><br /><ul><li>przyrównanie <span style="font-style: italic;">JavaBean</span> do <span style="font-style: italic;">POJO</span> - <a href="http://en.wikipedia.org/wiki/JavaBean"><span style="font-style: italic;">JavaBean </span></a>to jest <span style="font-style: italic;">POJO</span>, ale <a href="http://en.wikipedia.org/wiki/Plain_Old_Java_Object"><span style="font-style: italic;">POJO </span></a>to niekoniecznie <span style="font-style: italic;">JavaBean</span>. </li><li>Spring to nie tylko DI, ale też <span style="font-style: italic;">AOP </span>i <span style="font-style: italic;">Abstraction Of Services</span>. Jest to znacząca różnica, szczególnie w momencie gdy historycznie porównujemy Spring do J2EE.<br /></li></ul>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-86171789170836843322010-01-15T10:43:00.000-08:002010-01-15T11:52:31.745-08:00ArithmeticOperationFactoryBeanW ostatnim projekcie miałem potrzebę zdefiniowania w konfiguracji <span style="font-style: italic;">Spring</span> prostych arytmetycznych zależności między wartościami <span style="font-style: italic;">propertiesów</span>. Nie ma sensu zdefiniowanie maksymalnej ilości wątków w puli <span style="font-style: italic;">ThreadPoolTaskExecutor</span> gdy nie skorelujemy jej z ilością połączeń puli do bazy danych (przy założeniu że nasze <span style="font-style: italic;">taski</span> korzystają z bazy danych). W moim przypadku poszczególny <span style="font-style: italic;">task</span> potrzebował dokładnie 2 połączenia do bazy danych.<br />Chciałem mieć możliwość wyrażenia, że dane <span style="font-style: italic;">property</span> ma mieć wartość 2 * wartość innego <span style="font-style: italic;">property</span>.<br />Po krótkim googlowaniu nie udało mi się nic znaleźć i stwierdziłem, że łatwo samemu coś takiego napisać. W tym celu stworzyłem własną klasę <span style="font-style: italic;">FactoryBean</span>, której zadaniem było wykonywanie operacji mnożenia na <span style="font-style: italic;">injectowanych</span> parametrach.<br /><pre class="brush: java"><br />private TypeConverter typeConverter = new SimpleTypeConverter();<br /><br />public void setParam1(Object param1) {<br />this.param1 = param1;<br />}<br /><br />public void setParam2(Object param2) {<br />this.param2 = param2;<br />}<br /><br />public Object getObject() throws Exception {<br />Object result = null;<br />BigDecimal param1Converted = convert(param1);<br />BigDecimal param2Converted = convert(param2);<br /> BigDecimal result = param1Converted.multiply(param2Converted);<br />return result;<br />}<br /><br />private BigDecimal convert(Object obj) {<br /> return (BigDecimal) typeConverter.convertIfNecessary(obj, BigDecimal.class);<br />}<br />public Class getObjectType() {<br /> return BigDecimal.class;<br />}<br /></pre><br />Wyglądało to całkiem,całkiem ale pomyślałem, że bardzo prosto byłoby zbudować bardziej generyczne rozwiązanie. Implementacja <span style="font-style: italic;">FactoryBean</span> dla innej 2-argumentowej operacji byłaby prawie identyczny, a jedynie różniłaby się oepracją na klasie <span style="font-style: italic;">BigDecimal</span>. Dodatkowo chciałem mieć możliwość specyfikowania typu danych wyniku operacji arytmetycznej.<br />Generyczny ArithmeticOperationFactoryBean:<br /><pre class="brush: java"><br />public abstract class ArithmeticOperationFactoryBean implements FactoryBean,<br /> InitializingBean {<br />private TypeConverter typeConverter = new SimpleTypeConverter();<br />private Object param1;<br />private Object param2;<br />private Class<?> resultType;<br /><br />public void setResultType(Class<?> resultType) {<br /> this.resultType = resultType;<br />}<br /><br />public void setParam1(Object param1) {<br /> this.param1 = param1;<br />}<br /><br />public void setParam2(Object param2) {<br /> this.param2 = param2;<br />}<br /><br />@Override<br />public void afterPropertiesSet() throws Exception {<br /> if (param1 == null || param2 == null) {<br /> throw new IllegalArgumentException(<br /> "operation arguments can not be null");<br /> }<br /> if(resultType == null){<br /> resultType = BigDecimal.class;<br /> }<br />}<br /><br />@Override<br />public Object getObject() throws Exception {<br /> Object result = null;<br /> BigDecimal param1Converted = convert(param1);<br /> BigDecimal param2Converted = convert(param2);<br /> BigDecimal operationResult = executeOperation(param1Converted,<br /> param2Converted);<br /> result = operationResult;<br /> if (resultType != null) {<br /> result = convert(operationResult, resultType);<br /> }<br /> return result;<br />}<br /><br />protected abstract BigDecimal executeOperation(BigDecimal param1Converted,<br /> BigDecimal param2Converted);<br /><br />@SuppressWarnings("unchecked")<br />private <T> T convert(Object obj, Class<t> type) {<br /> return (T) typeConverter.convertIfNecessary(obj, type);<br />}<br /><br />private BigDecimal convert(Object param) {<br /> return convert(param, BigDecimal.class);<br />}<br /><br />@Override<br />public Class<?> getObjectType() {<br /> return resultType;<br />}<br /><br />@Override<br />public boolean isSingleton() {<br /> return true;<br />}<br /><br />}<br /></pre><br /><span style="font-style: italic;">FactoryBean</span> odpowiedzialny za operacje:<br /><pre class="brush: java"><br />public class MultiplicationFactoryBean extends ArithmeticOperationFactoryBean {<br /><br />@Override<br />protected BigDecimal executeOperation(BigDecimal param1,<br /> BigDecimal param2) {<br /> return param1.multiply(param2);<br />}<br />}<br /></pre><br />Użycie takiego <span style="font-style: italic;">FactoryBean</span> w konfiguracji wygląda w następujący sposób:<br /><pre class="brush: xml"><br /><beans><br /><br /><bean id="foo" class="java.lang.Double"><br /> <constructor-arg value="12"></constructor-arg><br /><br /></bean><br /><br /><bean id="myInteger" class="MultiplicationFactoryBean"><br /> <property name="param1" ref="foo"></property> <br /> <property name="param2" value="2"></property><br /> <property name="resultType" value="java.lang.Integer"></property><br /></bean><br /><br /></beans><br /></pre><br /><br />Konfiguracja definiuje w <span style="font-style: italic;">context</span> dwa <span style="font-style: italic;">beany</span>:"foo" typu <span style="font-style: italic;">java.lang.Double</span> oraz "myInteger" typu <span style="font-style: italic;">java.lang.Integer</span>. Wartość foo jest 24.<br /><br />Zamiast dziedziczenia może lepiej byłoby zastosować tu wzorzec <span style="font-style:italic;">strategy</span>, ale jest to w takim przypadku chyba pomijalne...<br />Dodatkowo, ekspsresywność rozwiązania nie rzuca na kolana, ale dla prostych zastosowań nadaję się moim zdaniem co najmniej przyzwoicie.milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-70537004813840692862009-10-22T10:07:00.000-07:002009-11-24T05:30:19.571-08:00Oracle + daty + JDBCOstatnio musiałem się zmierzyć z problemem związanym z obsługą dat w ORACLE 10. Wprowadzenie do tego problemu można znaleźć <a href="http://jmilkiewicz.blogspot.com/2009/06/oracle-i-hibernate.html">tutaj</a>. W dużym skrócie: w przypadku <span style="font-style: italic;">insert</span> do kolumny DATE wartości typu <span style="font-style: italic;">java.sql.Timestamp</span>, czyli z poziomu Hibernate zapisujemy <span style="font-style: italic;">property</span> typu <span style="font-style: italic;">java.sql.Timestamp</span> lub<span style="font-style: italic;"> java.util.Calendar/java.util.Date</span> z annotacją <span style="font-style: italic;">@Temporal(TemporalType.TIMESTAMP)</span> ), do bazy trafia zapisana przez nas data z czasem, ale bez milisekund - co jest zrozumiałe, bo DATE nie trzyma milisekund. Jeśli jednak spróbujemy odczytać zapisaną przez nas uprzednio krotkę, podając w warunku <span style="font-style: italic;">where </span>zapisaną przed chwilą wartość - wynik jest pusty!<br />Pewnym popularnym rozwiązaniem, jest ustawienie specjalnego <span style="font-style: italic;">property </span>dla <span style="font-style: italic;">ORACLE 9/10 JDBC driver</span>:<em> oracle.jdbc.V8Compatible=true</em>. Konsekwencje użycia tego ustawienia można znaleźć <a href="http://jmilkiewicz.blogspot.com/2009/06/oracle-i-hibernate.html">tu.<br /></a>Wyżej wymieniony problem można samemu zdiagnozować za pomocą testu.<br />Środowisko testowe korzysta z Oracle 10 g (10.2.0), JDBC Driver 10.2.0.4 a same testy są uruchamiane w <span style="font-style: italic;">JUNIT 4</span> za pomocą <span style="font-style: italic;">Spring TestContext Framework</span>.<br />Encja, która będzie służyć do testów:<br /><pre class="brush: java"><br />@Entity<br />@Table(name = "TestTable")<br />@SequenceGenerator(name = "seq_id", sequenceName = "SQ_TT")<br />public class TestEntity {<br />@Id<br />@GeneratedValue(generator = "seq_id")<br />Long i;<br /><br />@Column<br />String state;<br /><br />@Column(columnDefinition = "date")<br />@Temporal(TemporalType.TIMESTAMP)<br />Date d;<br /><br />public TestEntity() {<br />}<br /><br />public TestEntity( String state, Date d ) {<br />this.state = state;<br />this.d = d;<br />}</pre><br />Test ukazujący w/w problem<br /><pre class="brush: java"><br />@Test<br />public void test1(){<br />TestEntity t = new TestEntity("foo",new Date());<br />sessionFactory.getCurrentSession().save( t );<br />sessionFactory.getCurrentSession().flush();<br />sessionFactory.getCurrentSession().clear();<br />t = (TestEntity) sessionFactory.getCurrentSession().createQuery( "from TestEntity where d=?").setTimestamp( 0, t.getD()).uniqueResult();<br />assertThat( t, notNullValue() );<br />}<br /></pre><br />Test ten kończy się <span style="font-style: italic;">failure</span>.<br />Można to łatwo poprawić, gdy "wyzerujemy" milisekundy<br /><pre class="brush: java"><br />TestEntity t = new TestEntity("foo",new Date());<br />sessionFactory.getCurrentSession().save( t );<br />sessionFactory.getCurrentSession().flush();<br />sessionFactory.getCurrentSession().clear();<br />Calendar cal = Calendar.getInstance();<br />cal.setTime( t.getD() );<br />cal.set( Calendar.MILLISECOND, 0 );<br />t = (TestEntity) sessionFactory.getCurrentSession().createQuery( " from TestEntity where d=?").setTimestamp( 0, cal.getTime()).uniqueResult();<br />assertThat( t, notNullValue() );<br /></pre><br />Wykonanie tego testu kończy się sukcesem.<br />Na tym niestety problem się nie kończy. Pomimo tego, że udało nam się uzyskać poprawny wynik, to potrzeba "zerowania" milisekund jest strasznie upierdliwe, ale co gorsze zapytanie takie nie wykorzystują indeksu, który byłyby założone na kolumnie typu DATE. Można się o tym przekonać wykonując następujące polecenia (użytkownik <span style="font-style: italic;">system</span>) :<br /><pre class="brush: sql"><br />select sql_id, child_number,sql_text from V$SQL where sql_text like '%from TestEntity%' order by last_load_time desc;<br /></pre><br />W wyniku dostaniemy sql_id oraz child_number, obie wartości służą jako input dla polecenia:<br /><pre class="brush: sql"><br />SELECT * FROM table(DBMS_XPLAN.display_cursor('<sql_id >',<child_number>));</pre><br />W przypadku gdy mielibyśmy stworzony indeks np. na 2 kolumnach (jedna z nich będzie typu DATE), to zapytania z warunkiem <span style="font-style: italic;">where, </span> które teoretycznie mogłyby wykorzystywać indeks (, <span style="font-style: italic;">np status=? and d>=? and d<? )</span> mogą się bardzo długo wykonywać.<br />Jeśli nawet ORACLE użyje indeksu to może zrobić to bardzo nieefektywnie.<br />Jest to spowodowane tym, że ORACLE będzie "promował" daty w indeksie do typu TIMESTAMP. Taka "promocja" powoduję, że przeglądnięcie indeksu jest nieefektywne. Widać to w planie zapytania:<br /><pre class="brush: sql">|* 6 | INDEX RANGE SCAN | MOJ_INDEX | 741 | | | <br />-------------------------------------------------------------------------------------------------------- <br /><br />6 - access("state"=:3)<br />filter((INTERNAL_FUNCTION("D")>=:1 AND INTERNAL_FUNCTION("D")<:2)) </pre><br />"Promocja" typu jest prezentowana jako <span style="font-style: italic;">INTERNAL_FUNCTION</span><br />Rozwiązaniem problemu (brak użycia indeksu oraz potrzeba "zerowania" milisekund) byłoby "wypchnięcie" do bazy z poziomu JDBC wartości typu DATE a nie TIMESTAMP.<br />Można to zrobić poprzez:<br /><ul><li>używanie funkcji <span style="font-style: italic;">to_date</span> - wymaga to ręcznego wprowadzania daty w postaci <span style="font-style: italic;">String</span><pre class="brush :java"><br />@Test<br />public void testToDate(){<br />TestEntity t = new TestEntity("foo",new Date());<br />sessionFactory.getCurrentSession().save( t );<br />sessionFactory.getCurrentSession().flush();<br />sessionFactory.getCurrentSession().clear();<br />String dateInStringFormat = createDateFormat( "yyyy-MM-dd HH:mm:ss" ).format( t.getD() );<br />t = (TestEntity) sessionFactory.getCurrentSession().createQuery( " from TestEntity where d=to_date(?,'YYYY-MM-DD HH24:MI:SS')").setString( 0, dateInStringFormat ).uniqueResult();<br />assertThat( t, notNullValue() );<br />}<br /></pre></li><li>użycie funkcji<span style="font-style: italic;"> cast as<br /></span> - <span style="font-style: italic;">cast </span>z TIMESTAMP na DATE nie tylko "rzutuje" jeden typ na drugi, ale także dokonuję zaokrąglenia:<pre class="brush :java"><br />@Test<br />public void testCastMilis500andAbove(){<br />Calendar calendar = Calendar.getInstance();<br />calendar.setTime( new Date() );<br />calendar.set( Calendar.MILLISECOND, 500 );<br />TestEntity t = new TestEntity("foo",calendar.getTime());<br />sessionFactory.getCurrentSession().save( t );<br />sessionFactory.getCurrentSession().flush();<br />sessionFactory.getCurrentSession().clear();<br />t = (TestEntity) sessionFactory.getCurrentSession().createQuery( " from TestEntity where d= cast (? as date)").setTimestamp( 0, calendar.getTime()).uniqueResult();<br />assertThat( t, nullValue() );<br />}<br /><br />@Test<br />public void testCastMillisBelow500(){<br />Calendar calendar = Calendar.getInstance();<br />calendar.setTime( new Date() );<br />calendar.set( Calendar.MILLISECOND, 499 );<br />TestEntity t = new TestEntity("foo",calendar.getTime());<br />sessionFactory.getCurrentSession().save( t );<br />sessionFactory.getCurrentSession().flush();<br />sessionFactory.getCurrentSession().clear();<br />t = (TestEntity) sessionFactory.getCurrentSession().createQuery( " from TestEntity where d= cast (? as date)").setTimestamp( 0, calendar.getTime()).uniqueResult();<br />assertThat( t, notNullValue() );<br />}<br /></pre> Nie objedzie się w takim przypadku od "zerowania" milisekund. W przypadku <span style="font-style: italic;">Criteria API</span> nie wiem w jaki sposób <span style="font-style: italic;">out-of -box</span> używać wywołań funkcji. Chyba trzeba by stworzyć własnego <span style="font-style: italic;">Criterion</span><br /></li><li>bezpośrednie użycie typu <span style="font-style: italic;">oracle.sql.DATE</span>:<br /><pre class="brush :java"><br />@Test<br />public void test3ImplicitOracleDateJDBC() throws SQLException{<br />TestEntity t = new TestEntity("foo",new Date());<br />sessionFactory.getCurrentSession().save( t );<br />sessionFactory.getCurrentSession().flush();<br />sessionFactory.getCurrentSession().clear(); <br />SimpleJdbcTemplate jdbcTemplate = new SimpleJdbcTemplate(dataSource);<br />Timestamp ts = new Timestamp(t.getD().getTime());<br />oracle.sql.DATE d = new oracle.sql.DATE(ts);<br />long queryForLong = jdbcTemplate.queryForLong( "select count(* ) from TestTable where d=?", d );<br />assertThat( queryForLong, equalTo( 1l ) ); <br />}<br /></pre><br />Skuteczne, ale niewygodne. W przypadku HQL trzeba by pewnie stworzyć <span style="font-style: italic;">custom type</span>, który byłby użyty przy <span style="font-style: italic;">bindowaniu </span> parametrów przy pomocy wywołania<br /><pre class="brush :java"><br />Query setParameter(int position, Object val, Type type);<br /></pre><br />W przypadku użycia <span style="font-style: italic;">Criteria API</span> chyba najwygodniejsze byłoby stworzenie własnego <span style="font-style: italic;">Criterion</span></li><li>przy użyciu driveraJDBC w wersji 11.1.0.7.0 zadziała taka konstrukcja:<br /><pre class="brush: java"><br />@Test<br />public void testImplicitType() throws SQLException{<br />TestEntity t = new TestEntity("foo",new Date());<br />sessionFactory.getCurrentSession().save( t );<br />sessionFactory.getCurrentSession().flush();<br />sessionFactory.getCurrentSession().clear();<br />Connection connection = sessionFactory.getCurrentSession().connection();<br />PreparedStatement preparedStatement = connection.prepareStatement( "select count(*) from TestTable where d=?"); <br />preparedStatement.setObject( 1,new Timestamp(t.getD().getTime()) ,java.sql.Types.DATE );<br />assertThat( preparedStatement.executeUpdate(), equalTo( 1 )); <br />}<br /></pre></li><li>Oracle JDBC 9/10 (dla 9 nie sprawdzałem) driver undocumented "feature":<br /><pre class="brush: java"><br />@Test<br />public void testSetTimeMilis500andAbove(){<br />Calendar calendar = Calendar.getInstance();<br />calendar.setTime( new Date() );<br />calendar.set( Calendar.MILLISECOND, 500 );<br />TestEntity t = new TestEntity("foo",calendar.getTime());<br />sessionFactory.getCurrentSession().save( t );<br />sessionFactory.getCurrentSession().flush();<br />sessionFactory.getCurrentSession().clear();<br />t = (TestEntity) sessionFactory.getCurrentSession().createQuery( " from TestEntity where d= ? ").setTime( 0, calendar.getTime()).uniqueResult();<br />assertThat( t, notNullValue() );<br />}<br /><br />@Test<br />public void testSetTimeMilisBelow500(){<br />Calendar calendar = Calendar.getInstance();<br />calendar.setTime( new Date() );<br />calendar.set( Calendar.MILLISECOND, 499 );<br />TestEntity t = new TestEntity("foo",calendar.getTime());<br />sessionFactory.getCurrentSession().save( t );<br />sessionFactory.getCurrentSession().flush();<br />sessionFactory.getCurrentSession().clear();<br />t = (TestEntity) sessionFactory.getCurrentSession().createQuery( " from TestEntity where d=?").setTime( 0, calendar.getTime()).uniqueResult();<br />assertThat( t, notNullValue() );;<br />}<br /></pre><br />Zgodnie z <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/sql/PreparedStatement.html#setTime%28int,%20java.sql.Time%29">dokumentacją</a> <span style="font-style: italic;">setTime()</span> powinno zostać skonwertowane na TIME, ale konwersja jest robiona na DATE! Dla <span style="font-style: italic;">Criteria API </span>trzeba by stworzyć własne <span style="font-style: italic;">Criterion</span>, aby móc wymusić na <span style="font-style: italic;">Hibernate</span> użycie <span style="font-style: italic;">setTime()</span> - <span style="font-style: italic;">Hibernate out-of-box</span> decyduje jaką metodę wywołac na <span style="font-style: italic;">PreparedStatement</span> na podstawie typu/annotacji <span style="font-style: italic;">property </span>na której ustawiamy warunek.<br /></li></ul>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-83011031815860985192009-10-17T12:10:00.000-07:002009-10-19T11:47:05.803-07:00JDD 09<h3 class="post-title entry-title">“Jak usprawnić model domeny wykorzystując jBPM?”</h3><p>Pierwsza prezentacja dotyczyła jBPM. Dotychczas nie miałem do czynienia z takiego typu narzędziami i uważałem je bardziej za sztukę dla sztuki, które tak na prawdę nadają się tylko na prezentację. Pomimo uprzedzeń, prezentacje uważam za bardzo udaną, zaczęła się od wyjaśnienia znaczenia podstawowych, groźnych skrótów: <em>BPM, BPEL, PDL</em>. Po tym nastąpiło przedstawienie konkretnego narzędzia: <em>JBoss jBPM</em>. Parę rzeczy wyglądało na prawdę obiecująco: procesy można modelować w przeglądarce i pod Eclipse. Szczególnie ta pierwsza opcja może być ciekawa dla analityków - "wyklikają" proces w przeglądarce a później developer może zaimportować go w Eclipse. Sam <em>jBPM</em> może być uruchamiany w trybie standalone lub embedded. Moduł <em>jBPM</em> Console pozwala monitorować i śledzić wykonanie proces w postaci wykresów/tabelek i innych wodotrysków w przeglądarce. Zachwyciłoby to nie jednego managera. Dodatkowo <em>jBPM Console</em> pozwala w łatwy sposób na tworzenie prototypów aplikacji wykorzystujących <em>jBPM</em>, np. zasymulowanie wysłania SMS można zrealizować poprzez utworzenie prostego formularza HTML, którego wysłanie wygeneruje input dla kolejnego etapu procesu. </p><p><br /></p><h3 class="post-title entry-title">“Sztuka messagingu"</h3><p>Pierwsza prezentacja jednej z gwiazd JDD Marka Richardsa, autora <a href="http://oreilly.com/catalog/9780596522056">Java Message Service</a>. Mark starał się przekonać nas (i mnie osobiście przekonał), że pomimo tego, że JMS API nie zmieniło się od 2002, JMS jest cały czas bardzo użyteczną technologią, a jej umiejętne wykorzystanie pozwala nam tworzyć wydajne/skalowalne/niezawodne i potencjalnie heterogeniczne systemy. <a href="http://www.youtube.com/watch?v=8kjbv2aW3w8">Tu</a> znajduje się zapowiedz prezentacji Marka, która pasuję bardzo do obu jego prezentacji na JDD. Na początku prezentacji wiało trochę nudą, i nie pomogło nawet odebranie wiadomości w Groovym w typowym przykładzie send/receive. O wiele ciekawsze okazało się omówienie podstawowych przypadków użycia JMS: </p><ul><li>integracja poprzez JMS pomiędzy aplikacjami/modułami napisanymi w potencjalnie różnych językach (nie chodzi to bynajmniej o wspomniane powyżej Groovy), np JAVA i .NET</li><li>skalowalność - tu szczególnie leży siła rozwiązań opartych na JMS. W przypadku komunikacji 2 komponentów: pierwszy wysyła komunikat do kolejki, a drugi komponent przetwarza ten komunikat. Dzięki takiemu podejściu można bardzo swobodnie i łatwo sterować ilością komponentów (<em>listnerów</em>) przetwarzających komunikaty</li><li>asynchroniczność - możliwość asynchonicznego powiadamiania zainteresowanych komponentów<br /></li></ul><p>Dalej pojawiło się parę slajdów związanych z 2 modelami JMS: <em>point-to-point</em> or <em>publish-subscibe</em>, dostępne typy wiadomości (tu pojawiła się uwaga: aby zapewnić interoperability można zapomnieć o <a href="http://java.sun.com/j2ee/1.4/docs/api/javax/jms/ObjectMessage.html">ObjectMessage</a>) oraz budowa wiadomości. Mark zwrócił także dodatkowo na ważną cechę związaną z używaniem JMS API: w odróżnieniu od JDBC, transakcyjna jest obiekt <a href="http://www.j2ee.me/j2ee/1.4/docs/api/javax/jms/Session.html">session</a> a nie <a href="http://java.sun.com/j2ee/1.4/docs/api/javax/jms/Connection.html">connection</a>. Zazwyczaj optymalnym rozwiązaniem ma być użycie pojedynczego connection i puli obiektów session. Po prezentacji rozmawiałem z Markiem i uświadomił mnie ,że 1 MessageListener= 1 session = 1 TCP connection (to ostanie sam zamierzam sprawdzić ). Oznacza to, że nie można przesadzać z liczbą równolegle odpalonych listnerów - Mark stwierdził. że dla mocno obciążonych systemów ma byc to około 20. Jeśli chodzi o technikalia to polecił używanie Jencks wraz z ActiveMQ lub <a href="http://static.springsource.org/spring/docs/2.5.6/api/org/springframework/jms/connection/CachingConnectionFactory.html">CachingConnectionFactory</a>.<br />Bardzo podobał mi się slajdy przedstawiające różne considerations związane z wykorzystywaniem JMS: </p><ul><li>trwałość komunikatów - wiadomości mają byc domyślnie persystentne co oznaczą, że są trzymane w persystentnym storage. Ma to znaczący wpływ na wydajność - przedstawił wykres prezentujący 4 000 persystentych komunikatów na sekundę do 11 000 nie persystentnych. <em>JMSDeliveryMode</em> można ustawić na <a href="http://java.sun.com/j2ee/1.4/docs/api/javax/jms/MessageProducer.html">MessageProducer</a> lub bezpośrednio na <a href="http://java.sun.com/j2ee/1.4/docs/api/javax/jms/Message.html">Message</a></li><li>użycie pojedynczej kolejki dla całego przetwarzania - architektura w której to wszystkie komponenty wrzucają swoje komunikaty do pojedynczej kolejki. Wiadomości z kolejki są odczytywane przez pojedynczy komponent pełniący rolę routera, który na podstawie "magicznego dyskryminatora" przechowanego w property wiadomości (a nie w treści wiadomości) deleguję wywołanie (wywołuję metodę) odpowiedniego komponentu. Problem w takim podejściu polega na tym, że pewne komponenty (potencjalnie o różnym znaczeniu) mogą wrzucać z różną częstotliwością swoje wiadomości (potencjalnie o różnej charakterystyce przetwarzania) do wspólnej kolejki. Może to bardzo negatywnie wpływać na response time oraz dodatkowo utrudni skalowania takiego systemu - nie za wiele pomogą tutaj triki z ustawieniami różnych priorytetów dla wiadomości, tym bardziej, że nie można wywłaszczać aktualnie przetwarzanych komunikatów. Idąc dalej, Mark stwierdził, że nie jest niczym egzotycznym definiowanie więcej niż 1 kolejki dla tych samych wiadomości - by zapewnić QoS</li></ul><p><br />Na końcu tej części stanowczo stwierdził, że JMS bardzo dobrze się nadaje gdy mówimy o technologiach związanych z integracją szczególnie pod kątem <em>interoperability</em>. Stwierdził, że dla rozwiązań działających w środowisku "inside firewall" JMS nadaję się o wiele bardziej niż web services, głównie ze względu na większą możliwość tuningu, niezawodność i mniejszą złożoność tworzenia takich systemów (?).<br />Na końcu pojawiła się wzmianka o REST w świecie JMS i pewna technlogiczno-syntaktyczna niespójność: co powinna robić metoda GET ? czy ma pobierać wiadomość z kolejki ? A jeśli tak to pobranie wiadomości jednocześnie usuwa ją z kolejki, a przecież wywołanie GET powinno być safe. Z tego co zapamiętałem to Active MQ oraz Websphere MQ mają posiadać pseudo REST API do swoich systemów <p><h3 class="post-title entry-title"></h3><h3 class="post-title entry-title">“Obsługa sytuacji wyjątkowych w systemach budowanych w technologii JEE”</h3><p>Prezentacja sponsora konferencji, na szczęści wyglądało to duże lepiej niż rok temu. Zaprezentowany materiał całkiem fajny, oparty na doświadczeniu, ale trochę mało świeży, szczególnie gdy ktoś ma do czynienia z lekkimi frameworkami, które stawiają na <em>unchecked exceptions</em>. Oprócz podstawowych informacji o tym jakie wyjątki logować, w jaki sposób logować, co logować itd mnie bardziej zainteresowały następujące zagadnienia: </p><ul><li>transakcyjność a pamięć podręczna - w sytuacji gdy nasz cache nie jest transakcyjny (czyli zawsze, chyba że się mylę ) to trzeba zadbać aby w przypadku rollback wycofać to co w ramach transakcji zmodyfikowaliśmy w cache. Sprawa wydaję się w ogólności dość skomplikowana , a z tego co się orientuję to w <em>hibernate 2nd level cache</em> potrafi sobie z czymś takim radzić out-of-box</li><li>transakcyjność a sesja - w przypadku gdy nasze przetwarzanie na serwerze zakończy się wyjątkiem - sesja może być niespójna - wyjątek mógł polecieć po tym jak coś z sesji zmodyfikowaliśmy. Rozwiązanie ma być oparte o filtr opakowywujący oryginalną sesje w wrapper, dostępny dla aplikacje, a faktyczny zapis do sesji jest realizowany w tym samym filtrze po zakończeniu przetwarzania</li><li>W przypadku standardowego logowania <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/sql/SQLException.html">SQLException </a>stacktrace/message wyjątku nie zawierają ani SQLState ani ErrorCode</li><li>W przypadku <a href="http://java.sun.com/products/servlet/2.3/javadoc/javax/servlet/ServletException.html">ServletException </a>nie jest logowany faktyczny powód wystąpienia, który można zobaczyć dobierając się do <em>rootCause</em></li><li>naruszenie więzów integralności na bazie nie koniecznie jest błędem typu unrecover , a może być po prostu błędem biznesowym - próba stworzenia 2 userów o tym samym login. który to musi być unikalny. W takim przypadku można taki wyjątek złapać i poprawnie obsłużyć. </li></ul><p></p><h3 class="post-title entry-title"></h3><h3 class="post-title entry-title">“Asynchroniczność, współbieżność i rozproszone przetwarzanie w Java EE – przykłady z użyciem technologii middleware Oracle: WebLogic Server, EclipseLink/TopLink JPA i Coherence"</h3><p>Prezentacja Waldka Kota miała pierwotnie składać się z 2 części: omówienie <em>WorkManager API</em> a później przedstawienie <em>Oracle Coherence</em>. Jednak ze względu na to, że widownia miała problemy z zebraniem się z obiadu oraz dużą liczbą pytań o <em>WorkManager API</em> część druga w ogóle się nie odbyła, a Waldek obiecał, że opiszę to co miał przygotowanie na temat Oracle Coherence na swoim blogu. Prezentacja zaczęła się od przypomnienia/uświadomienia czemu nie powinno się tworzyć wątków w środowisku JEE. Z <em>WorkManager API</em> miałem styczność jakiś czas temu przy tworzeniu aplikacji JEE uruchamianych na "ulubionym"<em> IBM Websphere</em>. Pierwotnie API to powstała w ramach <a href="http://www.jcp.org/en/jsr/detail?id=237">JSR 236</a>, które aktualnie zostało zaniechane i z tego co opowiadał Waldek są jakieś zawirowania w tej sprawie w związku z JEE 6. Do tego czasu <em>Websphere</em>(min 6.0, w wersji 5.0 ma podobny mechanizm <em>Asynchronous Beans</em>)oraz <em>Weblogic</em> (min 9.0) wspierają <em>CommonJ Timer and Work Manager for Application Servers</em> (oprócz <em>Work Manager API </em>mamy do dyspozycji <em>Timer API</em>). Jest to na prawdę silna broń, ale nie do końca wierzę aby nadawała się do tworzenia aplikacji <em>batchowych</em>. Niespecjalnie wgłębiałem się w dostępne opcje konfiguracyjne <em>WorkManager</em> na <em>Websphere</em>, ale to co pokazywał Waldek na przykładzie serwera Weblogic robi naprawdę wrażenie: przypięcie <em>WorkManager</em> do wielu aplikacji, pojedynczej aplikacji, <em>servletu</em>, ziarna <em>EJB</em>, czy nawet metody <em>EJB</em>, określanie parametrów definiujących min/max ilość wątków, ustawienie <em>stuck time</em> i jeszcze pewnie parę innych.<br />Co ważne podkreślenia <em>WorkManager API</em> uruchamia zadania w kontekście JEE: same zadanie jest wykonywane w kontekście (<em>security</em>, <em>classpath</em>, <em>naming</em>) w jakim zostało ono zlecone do wykonania.<br />Specyfikacja wspomina także o <em>Remoteable WorkManager</em> i <em>RemoteWorkItem</em> - delegowanie zadań do zdalnych węzłów w klastrze. Websphere nie wspiera takiej funkcjonalności, jestem ciekaw jak to jest w <em>Weblogic</em>. </p><p><br /></p><h3 class="post-title entry-title"></h3><h3 class="post-title entry-title">Efektywne przeglądy kodu dla developerów Java używających metodologii z rodziny agile</h3><p>Bardzo ciekawa prezentacja, na temat <em>code review</em>. Wojtek na podstawie własnych doświadczeń, przestawił nie tylko na czym polega nowoczesny proces <em>code review</em>, ale co najważniejsze wiele wskazówek związanych z wprowadzaniem <em>code review</em> do organizacji - nic na siłę, metoda małych kroczków, trzeba dostosowac proces do zespołu (zmotywowany i samo-organizujący się zespół :) ), sposób wybieranie <em>reviewerów</em>. Pomimo wielu zasadniczych zalet: mentoring mniej doświadczonych/nowych członków zespołu, budowanie bazy wiedzy, zwiększenie <em>collective ownership</em> kodu, ewentualnie wykrywanie błędów, wprowadzenie <em>code review</em> niestety daje mało mierzalne rezultaty - co często może powodować opór managementu. <em>Code review</em> nie powinno w żadnym przypadku zastępowac statycznej analizy kodu, które to powinna odbywać sie przed <em>review</em> w celu "wyczyszczenia" kodu z naruszeń. <em>Code review</em>, które miałoby się odbywać "tradycyjnie" w postaci spotkania, na którym obecny miałby być cały zespól przeglądający wydrukowany kod, ma być mało skuteczne i problematyczne w sensie logistycznym, o wiele lepiej wykorzystać do tego narzędzia ,np. <a href="http://www.atlassian.com/software/crucible/">Crucible</a>. Przyznam, że narzędzie zrobiło na mnie duże wrażenie - wszystko wyglądało bardzo intuicyjnie i prosto, a jednocześnie dobrze dostosowane na potrzeby developerów. Bardzo ciekawie wyglądało porównanie <em>code review</em> i <em>pair programming</em>- obie techniki dotyczą pracy 2 osób nad kawałkiem kodu, jednak tak na prawdę więcej je od siebie różni niż łączy... Z projektów darmowych podobno warto sprawdzić: <a href="http://code.google.com/p/rietveld/">rietveld</a>, <a href="http://www.inso.tuwien.ac.at/projects/reviewclipse/">reviewClipse</a> a sam kiedyś instalowałem <a href="http://code.google.com/p/jupiter-eclipse-plugin/">jupiter</a></p><p><br /></p><h3 class="post-title entry-title"></h3><h3 class="post-title entry-title"></h3><h3 class="post-title entry-title">“Testowanie z Groovy”</h3><p>Prezentacja kolejnej z gwiazd JDD Scotta Davisa. Prezentacja mnie rozczarowała, po standardowej opowieści o tym, że na JVM można uruchamiać aplikacje napisane w: JRuby, JavaScript, JPython,Java FX, Scala oraz kilu żartach prowadzącego, zostało kilkanaście minut na wpomnienie o tym czym jest <a href="http://en.wikipedia.org/wiki/Behavior_Driven_Development">BDD</a> i pokazanie trywialnego kodu w Groovy z wykorzystaniem paradygmatu: given... : when... : then ... and ... </p><p><br /></p><h3 class="post-title entry-title"></h3><h3 class="post-title entry-title">Architektura Resource-Oriented (ROA) i REST</h3>ostatnia prezentacja JDD. Tym razem Scott opowiadał o REST i ROA. Zaczęło sie od typowych żartów na temat <em>SOAP</em>, które miało umożliwiać szybkie i wygodne tworzenie interoperable web services. Po tym jak wylano wiadro pomyj na <em>SOAP</em>, Scott pokazał z jaką łatwością konsumuje się REST webservices z poziomu Groovy, szczególnie gdy korzystamy z <a href="http://groovy.codehaus.org/api/groovy/util/XmlSlurper.html">XMLSlurper</a>. Po przedstawieniu podstawowych pojęć związanych z REST, Scott przedstawił wiele przykładów wykorzystania ROA: Ebay, Amazon, Twitter, Yahoo, <a href="http://code.google.com/intl/pl/apis/gdata/">GData</a>. Szczególnie polecał przyjrzenie się Google Calendar Data API, które uważa za modelowe przykład wykorzystania ROA, szczególnie gdy sami będziemy projektować REST based systems.milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com2tag:blogger.com,1999:blog-9185236033651750735.post-66026057730414745752009-10-12T03:35:00.000-07:002010-02-17T06:22:41.265-08:00Dostęp do Websphere Remote EJB 2.1 z JAVA SEPotrzebowałem stworzyć małą aplikację J2SE do wołania zdalnych sesyjnych bezstanowych EJB uruchomionych na Websphere 6.1.0.19. W mojej poprzedniej pracy juz robiłem coś takiego, ale wtedy aplikacja działała na SUN JRE. Tym razem podstawowym wymaganiem było użycie IBM JRE. Skoro serwera aplikacyjny oraz JRE są od tego samego dostawcy to sprawa powinna być prosta.... tymbardziej, że aplikacja była trywialnana a dodatkowo nie miałem potrzeby korzystać z żadnych z <em>enterprise features</em> jak bezpieczeństwo czy transakcyjność.<br />Dodatkowo EAR zawierający EJB, do którego trzeba się było dobierać nie był mojego autorstwa i nie dostarczono dla niego jara klienckiego - ze <em>stubami</em>, ale te można samemu wygenerować za pomocą <em>ejbdeploy.</em><br />Za wszelką cene chciałem uniknąć sytuacji, w której to musiałbym uruchamiać moją aplikację za pomocą <em>client container</em> albo wykonywać jakies inne, dziwne kroki w fazie developmentu/deploymentu.<br />Po przeczytaniu <a href="http://publib.boulder.ibm.com/infocenter/wasinfo/v6r1/index.jsp?topic=/com.ibm.websphere.base.doc/info/aes/ae/ccli_appclients.html">dokumentacji</a> wyglądało to wszystko extra skomplikowanie. Tymbardziej, że Weblogic już od dawna udostępniał swoim użytkownikom lekkiego <em>client.jar </em>(czy jakoś tak) do takich celów.<br />Czemu prosta rzecz nie może byc prosta?<br />Po wielu próbach i błędach udało mi się w końcu przygotować aplikację. Oprócz jara ze <em>stubami, classpath</em> zawiera następujące pozycje:<br /><pre class="brush: java"><br />ecore-2.3.0-v200706262000.jar<br />emf-2.1.0.jar<br />com.ibm.ws.webservices.thinclient_6.1.0.jar<br />com.ibm.ws.runtime_6.1.0.jar<br /></pre><br /><br />ecore-2.3.0-v200706262000.jar jest do ściągnięcia z mvnrepository, pozostałe 3 są skopiowane z WebSphere.<br />Dodatkowo bardzo ważna jest kolejność na classpath: com.ibm.ws.webservices.thinclient_6.1.0.jar <strong>musi</strong> byc przed com.ibm.ws.runtime_6.1.0.jar, ze względu na to, że klasa <em>com.ibm.ejs.ras.Tr</em> znajduję się w tych obu jarach.<br /><br />Odwołanie do EJB jest realizowane w następujacy sposób:<br /><pre class="brush: java"><br />Context initialContext = new InitialContext();<br />StringBuilder builder = new StringBuilder("corbaname:iiop:");<br />builder.append(serverIp);<br />builder.append(":");<br />builder.append(serverPort);<br />builder.append("#");<br />builder.append(ejbJNDIName);<br />Object lookUpResult = initialContext.lookup(builder.toString());<br />HomeInterface home = (HomeInterface) javax.rmi.PortableRemoteObject.narrow(<br /> lookUpResult, HomeInterface.class);<br /></pre><br /><em>serverIp</em> - adres serwera na którym jest zdeployowany EJB<br /><em>serverPort</em> - port do komunikacji RMI (domyslnie 2809)<br /><em>ejbJNDIName</em> - nazwa JNDI zdalnego EJB<br /><br />Dodatkowo dla serwera, którego EJB będziemy wywoływać należy dodać parę:adres ip, nazwa serwera do etc\hosts.<br /><br />Jedyne co mi się nie podoba w przygotowanym przeze mnie rozwiązaniu to potrzeba umieszczenia na <em>classpath</em> w/w bibliotek z Websphere (sam com.ibm.ws.runtime_6.1.0.jar zajmuję prawie 60 MB).<br />Podobno w Websphere 7.0 został przygotowany jar kliencki, ale osobiście nie mam jak na razie dostępu do tej wersji.<br /><br />UPDATE: przeglądając <a href="http://publib.boulder.ibm.com/infocenter/ieduasst/v1r1m0/topic/com.ibm.iea.was_v6/was/6.1/Architecture/WASv61_Componentization.pdf">materiały </a>związane z <span style="font-style:italic;">classloadingiem</span> dla WAS 6.1 natknąłem się na info dotyczące "thin client library" dla WAS 6.1. Składają się na to 2 jary: com.ibm.ws.admin.client_6.1.0.jar (34 Kb) oraz com.ibm.ws.webservices.thinclient_6.1.0.jar (15 Kb) siedzące w <INSTALL_ROOT>/runtimes. Po usunięciu z classpath jarów wymienionych na początku posta (ecore-2.3.0-v200706262000.jar, emf-2.1.0.jar, com.ibm.ws.webservices.thinclient_6.1.0.jar, com.ibm.ws.runtime_6.1.0.jar) i podegraniu na ich miejsce tych z <INSTALL_ROOT>/runtimes wszystko działa bez zmian.milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-39341190147450226902009-09-16T13:38:00.000-07:002011-01-17T01:12:43.914-08:00Websphere 6.1 remote SLSB między 2 EARW aplikacji EAR na websphere 6.1 stanęliśmy przed problemem integracji z pewnym zewnętrznym modułem. Początkowo planowałem owy moduł w postaci zbioru <em>jarów</em> wrzucić bezpośrednio do aplikacji i korzystać z niego poprzez przygotwaną <em>facade</em>. Jednak gdy otrzymałem już moduł to wiedziałem ,że takie podejście może się nie sprawdzić. Moduł ważył ponad 44 MB, a dodatkowo zawierał takie <em>jary</em> jak: <em>servlet.api, jta.jar</em> oraz parę innych z <em>j2ee</em>, ale także pewne biblioteki (m.in <em>stax,jaxb,spring, hibernate, xerces, log4j</em>) w innych wersjach niż te z których do teraz korzystałem... bez OSGI sądzę, że nie ma szans pogodzić ze sobą mojej aplikacji i modułu w ramach pojedynczego EAR.<br />Jedyne co udało się wykombinować to wrzucić nowy moduł jako osobny EAR, na ten sam serwer, na którym jest zdeployowana aplikacja. Oczywiście trzeba było obmyślić w jaki sposób aplikacja będzie mogła się teraz komunikowac z modułem. Najprostsze wydawało się stworzenie remote stateless session EJB 2.1 (2.1, bo nasz serwer nie ma zainstalowanego packa do EJB 3.0).<br />Skoro i tak juz został utworzony interfejs <em>POJO</em>, który miał wystawiać wszystkie usługi to wystarczyło stworzyć (wyklikać) EJB, którego remote interfejs pokrywałby się z tym uprzednio przygotowanym (z jedyną modyfikacją, że każda z metod musi deklarowac RemoteException).<br />Dostawca modułu wszystko ładnie przygotował - otrzymałem <em>client jar</em> z interfejsami oraz stubami, a także dodatkowo dostarczono nawet klienta testowego. Wszystko było super do momentu kiedy spróbowałem wywołać z mojej aplikacji metodę interfejsu modułu. Skoro funkcjonalność modułu jest dostępna przez remote EJB to odwołanie do takiego komponentu wydaję się bezproblemowe z poziomu Spring:<pre class="brush: java"><br /><xml:namespace prefix = jee /><?xml:namespace prefix = jee /><br /><jee:remote-slsb id="modulFacade" name="ejb/ModulFacade" ref="false" interface="xxx.yyy.zzz.ModuleFacadePojo"/><br /></pre><br /><br />Konsumpcja <em>remote stateless session EJB</em> w taki sposób ma parę zasadniczych zalet:<ul><br /><li>nie musimy pisać kodu, który robi <em>lookup</em>, wywołuję <em>create(), </em>cachuje <em>home</em></li><br /><li>nasz kod korzystający z <em>modulFacade</em> nie musi zajmować się obsługą <em>RemoteException</em><br /></li></ul><br /><br />Jednak po wywołaniu motody na <em>beanie modulFacade</em> leciały wyjątki: <em>ClassCastException</em> lub <em>NoSuchMethodException</em>. Szczególnie ten drugi wyjątek dał mi do myślenia i zasugerował, że wyjątki te mogą wynikac z moich ulubionych problemów związanych z <em>classloadingiem</em>. Przygotowana przez dostawce modułu aplikacja testowa w postacie <em>war</em> działała poprawnie, jednak tam dostęp do EJB był zrealizowany poprzez wygenerowaną (pewnie z RAD) klasę <em>Util</em>, która zajmowała się <em>lookup(), PortableRemoteObject.narrow(), create(). </em>Na potrzeby testu, spróbowałem wykorzystać tą klasę w mojej aplikacji i okazało się ,że tym razem wszystko działa bez problemu...<br /><br /><br /><br /><p>W takim razie problem leżał w Spring lub w niewłaściwej konfiguracji elementu <em>jee:remote-slsb</em>.Po chwili googlowania udało mi się znaleźć odpowiedni <a href="http://forum.springsource.org/showthread.php?t=17203">post </a>na forum, wraz z rozwiązaniem.</p><br /><p>Chodzi o ustawienie atrybutu <em>home-interface:</em><br /></p><pre class="brush: java"><br /><xml:namespace prefix = jee /><?xml:namespace prefix = jee /><jee:remote-slsb id="modulFacade" name="ejb/ModulFacade" ref="false" interface="xxx.yyy.zzz.ModuleFacadePojo" home-interface="xxx.yyy.zzz.ModulFacadeHome"/></pre><br />Pobrany z JNDI objekt będący home remote SLSB aplikacji-modułu jest stubem, który został załadowany przez classloader aplikacji-modułu. Dalsze wywołania na tym obiekcie skutkują tworzeniem obiektów, których klasy są ładowane tym samym classloaderem. W ten sposób dojdziemy do momentu, w którym to dana metod biznesowe zwraca nam obiekt, którego klasa znajduję się w <em>client jar</em>, ale został załadowany przez classloader aplikacji-modułu.<br />Teraz jeśli w naszej aplikacji zrobiony będzie <em>cast</em> to dostaniemy ClassCastException.<br />Podobnie to wszystko wygląda gdy nasza metoda biznesowa, będzie brała jako parametr obiekt klasy znajdujący się w <em>client jar.</em> W takim przypadku sygnatura metody nie będzie się zgadzać bo 2 identyczne klasy załadowane przez 2 różne classloadery są niezgodne.<br />Całość bardzo ładnie widać w trybie <em>debug:</em><pre class="brush: java"><br />protected Object lookup() throws NamingException {<br /> Object homeObject = super.lookup();<br /> if (this.homeInterface != null) {<br />try {<br />homeObject = PortableRemoteObject.narrow(homeObject, this.homeInterface);<br />}catch (ClassCastException ex) {<br /> throw new RemoteLookupFailureException(<br />Could not narrow EJB home stub to home interface [" + this.homeInterface.getName() + "]", ex);<br /> }<br /> }<br /> return homeObject;<br />}<br /></pre><br />Wywołanie <em>lookup()</em> zwraca nam objekt będacy interfejsem <em>home</em> EJB.<br />Jednak gdy podejrzymy: <em>homeObject.getClass().getClassLoader()</em> to zobaczymy <em>classloader</em>, który załadował aplikacje modułu. Dopiero wywołanie <em>PortableRemoteObject.narrow</em> spowoduję, że wartością <em>homeObject.getClass().getClassLoader()</em> będzie <em>classloader</em> naszej aplikacji. Z tego co się doczytałem zachowanie takie jest spowodowane tym, że Websphere optymalizuję wywołanie przez zdalne EJB w ramach dwóch aplikacji znajdujących się w tym samym JVM. Jak będę miał chwilę to faktycznie można to sprawdzić: należałoby wrzucic <em>client jar</em> do <em>shared-library</em> i zobaczyć czy faktycznie uda się uzyskać<em> pass-by-reference</em>.<br />Dodatkowo gdy każda z tych aplikacji byłaby uruchamiana na różnych JVM to podawanie <em>home-interface</em> ma także być zbędne, czyli bez wywołania PortableRemoteObject.narrow wszystko ma działać...to też można by sprawdzić.<br /><br />Update: przypadkowo trafiłem na całkiem sensowny opis mechanizmu/rozwiązań: <a href="http://fixunix.com/websphere/344774-local-interfaces-same-jvm-but-different-ear-issue.html">http://fixunix.com/websphere/344774-local-interfaces-same-jvm-but-different-ear-issue.html</a>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-61109503943921210122009-07-14T01:38:00.000-07:002009-11-25T06:06:13.954-08:00Websphere 6.1, SWS i MTOMWebsphere w wersji 6.1 obsługuje jedynie SAAJ w wersji 1.2. Aby móc korzystac z MTOM potrzebny jest SAAJ 1.3. Z tego co znalazłem w sieci jest dostępny odpowiedni <a href="http://www-01.ibm.com/support/docview.wss?rs=180&uid=swg21264563">feature pack</a>, który ma obsługiwac SAAJ 1.3, MTOM oraz wiele więcej. Jednak instalowanie feature packów od IBM często kończy się różnie (tzn czasami nie działają, a jeszcze częściej trzeba instalowac kolejne packi/łaty itd). Dlatego zdecydowałem się na "własną instalację" SAAJ 1.3.<br />Na podstawie moich wcześniejszych <a href="http://jmilkiewicz.blogspot.com/2009/03/spring-webservices-on-webspeher-610x.html">walk z SWS pod Websphere</a>, wszystko okazało się całkiem proste.<br />Do jarów wchodzących w skład <em>shared-library</em> (definujących biblioteki nowego <em>classloadera</em>)dodajemy dodatkowo:<br /><ul><li>saaj-api-1.3.jar </li><br /><li>saaj-impl-1.3.jar ( implementacja Suna )</li><br /><li>jaxp-ri-1.4.2.jar (do ściągnięcia z <a href="http://download.java.net/maven/1/com.sun.xml.parsers/jars/">http://download.java.net/maven/1/com.sun.xml.parsers/jars/</a>)<br /></li></ul><p>Dodatkowo pozwalamy SWS na automatyczne wykrycie implementacji <em>javax.xml.soap.MessageFactory - </em>czyli z naszej konfiguracji springowej wywalamy <em>bean</em> "messageFactory". </p><p>Alternatywnie, MTOM równie działa, gdy wymienione wyżej biblioteki wrzucimy do <em>WEB-INF/lib</em> oraz ustawimy ładowanie klas z bibliotek aplikacji przed tymi z bibliotek serwera (PARENT-LAST dla modułu webowego).<br /><br />W przypadku ustawienia <em>PARENT_LAST </em>i używania JAXB 2 pojawiają się jednak pewne problemy, które objawiają się wyjątkiem:<br /></p><pre class="brush: java"><br />Caused by: java.lang.VerifyError<br />at com.sun.xml.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl.<clinit>(RuntimeBuiltinLeafInfoImpl.java:224)<br />at java.lang.J9VMInternals.initializeImpl(Native Method)<br />at java.lang.J9VMInternals.initialize(J9VMInternals.java:194)<br />at com.sun.xml.bind.v2.model.impl.RuntimeTypeInfoSetImpl.<init>(RuntimeTypeInfoSetImpl.java:61)<br />at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.createTypeInfoSet(RuntimeModelBuilder.java:127)<br />at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.createTypeInfoSet(RuntimeModelBuilder.java:79)<br />at com.sun.xml.bind.v2.model.impl.ModelBuilder.<init>(ModelBuilder.java:151)<br />at com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder.<init>(RuntimeModelBuilder.java:87)<br />at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getTypeInfoSet(JAXBContextImpl.java:422)<br />at com.sun.xml.bind.v2.runtime.JAXBContextImpl..<init.>(JAXBContextImpl.java:286)<br />at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:139)<br />at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:117)<br />at com.sun.xml.bind.v2.ContextFactory.createContext(ContextFactory.java:188)<br />at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)<br />at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:79)<br />at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)<br />at java.lang.reflect.Method.invoke(Method.java:618)<br />at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:133)<br />at javax.xml.bind.ContextFinder.find(ContextFinder.java:286)<br />at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:372)<br />at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:337)<br />at org.springframework.oxm.jaxb.Jaxb2Marshaller.createJaxbContextFromContextPath(Jaxb2Marshaller.java:311)<br /></pre><br />Po dłuższej chwili debugowania problemem okazała się klasa <em>javax.xml.namespace.QName</em>.<br />JAXB 2 przy starcie dobiera się do stałych klasy <em>javax.xml.datatype.DatatypeConstants</em>.<br />Klasa ta sama w sobie jest ładowana z Websphere(../runtimes/base_v61/java/jre/lib/xml.jar), a typem tych stałych jest klasa <em><strong>javax.xml.namespace.QName</strong></em>, która też znajdują się w Webpshere (../runtimes/base_v61/lib/j2ee.jar). Czyli przy odwołaniu się do do klasy <em>javax.xml.datatype.DatatypeConstants</em>, zostanie ona wraz ze swoimi zależnosciami ( m.in. klasą <em>javax.xml.namespace.QName</em> ) załadowana przez <em>classloader</em> Websphere.<br />Dodatkowo klasa <em>javax.xml.namespace.QName</em> znajduje się w <em>Jsr173_api-1.0.jar/stax-api-1.0.1.jar</em>, a przy ustawieniu <em>Parent-Last</em> nasza aplikacja najpierw załaduje ją właśnie stamtąd. Dojdzie do sytuacji, w której "skontatkują" się ze sobą 2 klasy (<em>javax.xml.namespace.QName</em>)o tej samej nazwie, o potencjalnie różnych definicjach, załadowane przez 2 różne <em>classloadery</em>.<br />Rozwiązaniem okazuję się wywalenie z naszej aplikacji biblioteki <em>Jsr173_api-1.0.jar/stax-api-1.0.1.jar </em>i podegranie na jej miejsce <em>stax-api-1.0-2.jar</em>(ta nie zawiera w sobie żadnych klas z pakietu <em>jaxax.xml.namespace</em>)<br /><br />UPDATE: na jednym ze środowisk pomimo w/w ustawień leciały wyjątki:<br /><pre class="brush: java"><br />Caused by:<br />java.lang.NoClassDefFoundError: javax.xml.transform.stax.StAXResult<br /> at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.getOutputHandler(TransformerImpl.java:416)<br /> at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:334)<br /> at org.springframework.xml.transform.TransformerObjectSupport.transform(TransformerObjectSupport.java:71)<br /> at org.springframework.ws.wsdl.wsdl11.provider.InliningXsdSchemaTypesProvider.getSchemaElement(InliningXsdSchemaTypesProvider.java:113)<br /> at org.springframework.ws.wsdl.wsdl11.provider.InliningXsdSchemaTypesProvider.addTypes(InliningXsdSchemaTypesProvider.java:101)<br /> at org.springframework.ws.wsdl.wsdl11.ProviderBasedWsdl4jDefinition.afterPropertiesSet(ProviderBasedWsdl4jDefinition.java:233)<br /> at org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition.afterPropertiesSet(DefaultWsdl11Definition.java:170)<br /> at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1369)<br /> at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1335)<br /> ... 39 more<br />Caused by:<br />java.lang.ClassNotFoundException: javax.xml.transform.stax.StAXResult<br /> at java.net.URLClassLoader.findClass(URLClassLoader.java:496)<br /> at com.ibm.ws.bootstrap.ExtClassLoader.findClass(ExtClassLoader.java:132)<br /> at java.lang.ClassLoader.loadClass(ClassLoader.java:631)<br /> at com.ibm.ws.bootstrap.ExtClassLoader.loadClass(ExtClassLoader.java:87)<br /> at java.lang.ClassLoader.loadClass(ClassLoader.java:597)<br /> at com.ibm.ws.classloader.ProtectionClassLoader.loadClass(ProtectionClassLoader.java:58)<br /> at com.ibm.ws.classloader.ProtectionClassLoader.loadClass(ProtectionClassLoader.java:54)<br /> at com.ibm.ws.classloader.CompoundClassLoader.loadClass(CompoundClassLoader.java:394)<br /> at java.lang.ClassLoader.loadClass(ClassLoader.java:597)<br /> ... 48 more<br /></pre><br />Klasa <span style="font-style: italic;">org.springframework.xml.transform.TransformerObjectSupport</span> korzysta z wywołania <span style="font-style: italic;">javax.xml.transform.TransformerFactory.</span><span style="font-style: italic;" class="annLine lineContent" title=""><span class="hl_identifier">newInstance</span>()</span>, przy czym faktyczna implementacja jest określona za pomocą mechanizmu <span style="font-style: italic;">ServiceLoader API</span>. Okazało się, że w obu jarach: jaxp-ri-1.4.2.jar oraz xalan-2.7.0.jar, istnieją wpisy definiujące implementacje dla interfejsu <span style="font-style: italic;">javax.xml.transform.TransformerFactory</span>. Dotychczas "szczęśliwie" wczytywana była implementacja <span style="font-style: italic;">org.apache.xalan.processor.TransformerFactoryImpl</span> z xalan-2.7.0.jar, ale na jednym ze środowisk zaczytano <span style="font-style: italic;">com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl</span> z jaxp-ri-1.4.2.jar. Nie miałem zbytnio ochoty wgłębiać się w ten temat (można poprzez <span style="font-style: italic;">system property</span> wymusić konkretną implementację , może można określić kolejność ładowania przez <span style="font-style: italic;">ServiceLoader API</span>) usunąłem plik META-INF\services\javax.xml.transform.TransformerFactory z jaxp-ri-1.4.2.jar i po kłopocie :)milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-14160048261315540272009-06-15T01:03:00.000-07:002009-10-20T04:09:11.467-07:00Oracle i HibernateW 2 ostatnich projektach dość intensywnie wykorzystujemy Hibernate jako ORM dla Oracle 10.2.0.1.0. (sterownik JDBC w wersji 10.2.0.4.0.) Takie rozwiązanie nie zawsze okazało się bezproblemowe. <ul><li>"Wrong column type in {nazwa schematu.nazwa kolumny} for column {nazwa kolumny}. Found: date, expected: timestamp". Walidacja schematu przez Hibernate<em>(hibernate.hbm2ddl.auto</em>=<em>validate)</em> wyrzuca wyjątek przy mapowaniu<br /><pre class="brush: java">@Temporal(TemporalType.TIMESTAMP)<br />private java.util.Date published;<br /></pre><br />gdy kolumna w bazie jest typu <em>DATE</em>.<br />Oracle w wersji 9.2 wprowadził nowy typ danych <em>TIMESTAMP, </em>ale jednocześnie wprowadził mapowanie <em>DATE</em> =><em>java.sql.Date (<br />javax.persistence.TemporalType.Date)</em>. Typ <em>java.sql.Date</em> posiada jedynie dane o dacie bez info o czasie, przy czym Oraclowy <em>DATE</em> przetrzymuje dane zarówno o dacie jak i o czasie. Sprawdzenie przez drvier JDBC czy <em>DATE</em> mapuje się na <em>java.sql.TimeStamp(<br />javax.persistence.TemporalType.TIMESTAMP)</em> jest <em>false</em> i rzucany jest wyjątek. Rozwiązań tego problemu jest kilka, przy czym większość z nich polega na modyfikacji definicji kolumn i/lub zmian w kodzie. Na nasze potrzeby najsensowniejsze okazało się wprowadzenie<span style="FONT-STYLE: italic"> property</span> (jako<span style="FONT-STYLE: italic"> property</span> połączenia lub <span style="FONT-STYLE: italic">property</span> systemowe) <strong>oracle.jdbc.V8Compatible=true</strong>. Oznacza to powrót do mapowania między <em>DATE </em>a <em>java.sql.TimeStamp </em>zgodnie z tym jak to było dla Oracle 8i (który nie miał typu <em>TIMESTAMP</em>). W przypadku konfiguracji <em>Spring</em> dla <em>c3p0</em> definicja <em>DataSource</em> wygląda mniej więcej następująco:<br /><pre class="brush: xml"><br /><bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSourcePool" method="close"><br /><property name="properties"><br /><props><br /> <prop key="oracle.jdbc.V8Compatible">true</prop><br /></props><br /></property><br /><property name="driverClass" value="${db.driver}"/><br /><property name="jdbcUrl" value="${db.url}"/><br /><property name="user" value="${db.username}"/><br /><property name="password" value="${db.pwd}"/><br />...<br /></bean></pre><br />Ważne jest zachowanie kolejności: najpierw <span style="FONT-STYLE: italic">property</span> <em>properties</em> wraz z <span style="FONT-STYLE: italic">entry</span> <em>oracle.jdbc.V8Compatible</em> ustawione na <em>true, </em>a dopiero później pozostałe <em>propertiesy.</em><br /></li><li>Zgodnie z <a href="http://www.oracle.com/technology/tech/java/sqlj_jdbc/htdocs/jdbc_faq.html#08_01">FAQ</a> <em>oracle.jdbc.V8Compatible</em> zostało wprowadzone aby zapewnić kompatybilność z Oracle 8.1, który nie posiadał typu Timestamp. Oznacza to, że przy przekazywaniu do bazy danych obiektu typu java.sql.Timestamp (z poziomu Hibernate w postaci property typu java.util.Timestamp lub property typu java.util.Calendar/ java.util.Date z annotacją @Temporal(TemporalType.TIMESTAMP) ) zostanie on przekonwertowany na typ bazodanowe DATE , który nie przechowuje milisekund. Czyli, jeśli mamy w bazie kolumne <em>ts</em> typu TIMESTAMP to pomimo mapowania:<pre class="brush: java"><br />@Column(name="ts)<br />java.sql.Timestamp ts;</pre>lub<pre class="brush: java"><br />@Column(name="ts)<br />@Temporal(TemporalType.TIMESTAMP)<br />java.util.Date/java.util.Calendar ts;</pre>insert/update property <em>ts</em> zapisze do bazy danych date z czasem ale bez milisekund.<br />Co ciekawsze przy odczycie, property <em>ts</em> posiada milisekundy !!! (zakres milisekund zależy od tego czy zamapowaliśmy na javowe Calendar, Date czy Timestamp) . Jeśli chcemy z poziomu JDBC przechowywać w Oracle daty wraz z czasem z milisekundami nie można użyć <em>oracle.jdbc.V8Compatible=true</em>. Aby móc jednocześnie walidować schemat<em> </em>wystarczy wskazać typ kolumny jako wartość <em>columnDefinition </em>w annotacji <em>@Column:</em><br /><pre class="brush: java"><br />@Column(columnDefinition="date")<br />@Temporal(TemporalType.TIMESTAMP)<br />private java.util.Date d;</pre>Dodatkowo warto pamiętac, że w przypadku zapisywania do kolumny DATE wartości typu java.sql.Timestamp (czyli z poziomu Hibernate property typu java.sql.Timestamp lub java.util.Calendar/java.util.Date z annotacją @Temporal(TemporalType.TIMESTAMP) ) do bazy zawsze trafi data z czasem bez milisekund. Jest to logiczne gdyż DATE nie trzyma milisekund. Jednak gdy najpierw zapiszemy do bazy wiersz, którego składową jest kolumna DATE wypełniana z pozimou javy wartością typu java.sql.Timestamp ( z poziomu Hibernate property typu java.sql.Timestamp lub java.util.Calendar/java.util.Date z annotacją @Temporal(TemporalType.TIMESTAMP) ), a następnie będziemy chcieli tą samą wartość wykorzystać w warunku <em>where, </em>może się okazać, że nie znajdziemy już takiej krotki. ORACLE najpierw dokona "promocji" wartości w kolumnie DATE do typu parametru wejściowego. W naszym przypadku wartości w kolumnie typu DATE zostaną "wypromowane" do typu TIMESTAMP, ponieważ nasz wejściowy parametr typu java.sql.Timestamp zostanie zamapowany po stronie bazy na typ TIMESTAMP, przy czym proces "promowanie" dodatkowo uzupełni milisekund zerami. Następnie dopiero zostanie wykonane porównywanie. Nawet jeśli ręcznie "wyzerujemy" milisekundy naszego wejściowego java.sql.Timestamp i w ten sposób znajdziemy żądany wiersz, to trzeba pamiętac, że w zapytaniu ORACLE nie użyje indeksu założonego na kolumnie typu DATE- indeks jest założony na typie DATE a nie TIMESTAMP. W przypadku gdy mielibyśmy włączone <em>oracle.jdbc.V8Compatible </em>takich problemów udałoby się uniknąć, ponieważ sterownik JDBC zamapuje nam typ java.sql.Timestamp na DATE. Więcej na ten temat możemy znaleźć <a href="http://forums.oracle.com/forums/thread.jspa?messageID=3611155">tu</a> i <a href="http://forums.oracle.com/forums/thread.jspa?messageID=1659839">tu</a></li><li>"Wrong column type in {nazwa schematu.nazwa kolumny} for column {nazwa kolumny}. Found: char, expected: varchar2(255 char)". Walidacja schematu przez Hibernate(hibernate.hbm2ddl.auto=validate) wyrzuca wyjątek przy mapowaniu:<br /><pre class="brush: java"><br />@Column(name="name")<br />private String name;</pre>gdy kolumna w bazie jest typu <em>char(x)</em> gdzie x>1.<br />Problem wynika z <a href="http://opensource.atlassian.com/projects/hibernate/browse/HHH-2304">błędu</a> w Hibernate, który mapuje bazodanowy typ <em>char</em> na javowy <em>Character</em>.<br />Gdy zmienimy mapowanie na<br /><pre class="brush: java"><br />@Column(name="name")<br />private char name;</pre><br />wszystko jest oki, ale będziemy ograniczeni do przechowywania/odczytywania pojedyńczego znaku...<br />Aby typ <em>char(x)</em> mapował się na <em>String</em> trzeba albo załadować patch opisany powyżej w zgłoszeniu, albo też wskazać konkretny typ bazdanowy na jaki będzie zamapowane nasze property.<br />W tym drugim przypadku wystarczy dopisać <em>columnDefinition</em><br /><pre class="brush: java"><br />@Column(name="name", columnDefinition="char")<br />private String name;</pre></li><br /><li>Criterion uwzględniające <em>escapowanie </em>znaków specjalnych do wyszukiwania za pomocą <em>like: <a href="http://levanhuy.wordpress.com/2009/02/19/providing-an-escape-sequence-for-criteria-queries/">http://levanhuy.wordpress.com/2009/02/19/providing-an-escape-sequence-for-criteria-queries/<br /></a></em>Trzeba jednak kod trochę zmodyfikować aby <span style="FONT-STYLE: italic">escapował</span> znak służący jako<span style="FONT-STYLE: italic"> escape character</span>, czyli w tym przypadku trzeba escapowac znak '\'.<br /></li><li>Wyszukiwanie <em>Clob</em> po zawartości. Posiadając mapowanie<pre class="brush: java">@Lob<br />@Column(name = "CONTENT", nullable = false)<br />private char[] content;<br /></pre>do typu bazodanowego <em>CLOB</em> można wyszukiwać <span style="FONT-STYLE: italic">Cloby</span> po zawartości. Nie chcę tutaj pisać na temat wydajności takiego rozwiązania, ale ogólnie jest to możliwe i działa. Wyszukiwanie przy pomocy <em>like</em> wygląda standardowo:<br /><pre class="brush: java">List list = session.createQuery(" from Obj where content like ?").setParameter(0, "%Ala%".toCharArray()).list();<br /></pre><br />W przypadku używanie <em>Criteria</em> najlepiej posłużyc się zmodyfikowaną klasą CriteriaEscape predstawioną powyżej. Dla <em>Clob</em>, które są zamapowane do <span style="FONT-STYLE: italic">char[] </span>trzeba dokonać małej poprawki:<br /><pre class="brush: java">public TypedValue[] getTypedValues(Criteria criteria,<br />CriteriaQuery criteriaQuery) throws HibernateException {<br />return new TypedValue[]{criteriaQuery.getTypedValue(criteria, propertyName, ("%" + value + "%").toCharArray())};<br />}<br /></pre><br />Idealnie wydaję się być usunięcie manualnej konkatenacji Stringów i użycie <span style="FONT-STYLE: italic">org.hibernate.criterion.MatchMode.toMatchString()</span>. Nie tylko będzie to bardziej eleganckie, ale też bardziej funkcjonalne - można by przekazać implementacje <span style="FONT-STYLE: italic">org.hibernate.criterion.MatchMode </span>w konstruktorze klasy i dzięki temu możemy łatwo sterować czy znak '%' ma być na początku/końcu czy i na początku i na końcu szukanej frazy.<br />Trochę inaczej to wygląda gdy spróbujemy wyszukiwać <em>Cloby</em> za pomocą operatora równości. Próba wykonania kodu:<br /><pre class="brush: java">Object object = currentSession.createQuery(" from Obj where content=?" ).setParameter(0, "ipsum".toCharArray()).setMaxResults(1).uniqueResult();<br /></pre><br />kończy się wyjątkiem: <em>java.sql.SQLException: ORA-00932: inconsistent datatypes: expected - got CLOB</em>. Nie da się ukryć, że typy faktycznie nie pasują. Można się posiłkować Oraclową funkcją <em>TO_CHAR</em>. W Oracle 10.1.0.4.2 takie zapytanie przejdzie:<br /><pre class="brush: java"><br />Object object = currentSession.createQuery(" from Obj where to_char(content)=?" ).setParameter(0, "ipsum".toCharArray()).setMaxResults(1).uniqueResult();<br /></pre><br />Oracle "po cichu" obetnie <em>CLOB</em> do pierwszych 4000 znaków i wtedy dokona porównania.<br />Od wersji 10.2.0.1.0 jednak w takim przypadku dostaniemy błąd:<br /><em>SQL Error: ORA-22835: Buffer too small for CLOB to CHAR or BLOB to RAW conversion (actual: {wartość powyżej 4000}, maximum: 4000)</em>. Oracle tym razem już nie obetnie "po cichu" CLOB do 4000 znaków, ale można takie zachowanie na wymusić:<br /><pre class="brush: java">Object object = currentSession.createQuery(" from Obj where to_char(substr(CONTENT,1,4000))=?" ).setParameter(0, "ipsum".toCharArray()).setMaxResults(1).uniqueResult();<br /></pre><br /></li><li>Przy okazji udało się natrafić na parę "niedoskonałości" <span style="FONT-STYLE: italic">Hibernate</span>, które zostały już jakiś czas temu znaleziono, ale nie poprawione<br /><ul><br /><li>Zdefiniowanie <span style="FONT-STYLE: italic">Criteria</span> na klasie, która nie jest zmapowana, nie powoduje wystąpienia żadnego wyjątku. Jest to dziwne biorąc pod uwagę, że w HQL coś takiego nie przejdzie. Problem został już dawno <a href="http://opensource.atlassian.com/projects/hibernate/browse/HHH-1665">zgłoszony</a></li><li><em>Hibernate</em> generuje nie poprawne zapytanie w przypadku projekcji definiującej alias, który jest taki sam jak nazwa <em>property</em> a jednocześnie zdefinujemy restrykcji na tym <em>property</em>. Objawia się to wyjątkiem <em>java.sql.SQLException: ORA-00904: "Y0_": invalid identifier</em>. Dotyczy to jedynie aliasów dla <em>root entity</em>. Problem juz został dawno <a href="http://opensource.atlassian.com/projects/hibernate/browse/HHH-817">zgłoszony</a>, przygotowano dla niego patch, ale bezpieczniej jest prefixowanie wszystkich restrykcji na root entity za pomocą aliasu"<em>this</em>"<br /><pre class="brush: java">ProjectionList projectionList = Projections.projectionList().add( Projections.id().as( "id" ) ).add( Projections<br /> .property( "name" ).as( "name" ) ).add( Projections.property( "surname" ).as( "surname" ) );<br />List result = currentSession.createCriteria(Author.class)<br /> .setProjection( projectionList )<br /> .add( Restrictions.eq( "this.surname", "Borchardt") )<br /> .setResultTransformer( new AliasToBeanResultTransformer(Author.class) )<br /> .list();<br /></pre>albo korzystanie z podlasy <em>AliasedProjection</em> napisanej przez Chris Federowicza i Kevin Schmidta<br /><pre class="brush: java"><br />ProjectionList projectionList = Projections.projectionList().add( new CustomPropertyAliasProjection("id","id"))<br /> .add( new CustomPropertyAliasProjection("name", "name")).add( new CustomPropertyAliasProjection("surname","surname" ));<br />List result = currentSession.createCriteria(Author.class)<br /> .setProjection( projectionList )<br /> .add( Restrictions.like( "surname", "Borchardt") )<br /> .setResultTransformer( new AliasToBeanResultTransformer(Author.class) )<br /> .list();<br /></pre><br />Problem ten nie dotyczy wszystkich baz, np HSQLDB radzi sobie bez problemu gdy warunek w <em>where</em> jest definiowany na aliasie a nie na kolumnie, czyli bład ten tam nie występuję. Ogólnie jest to kolejny przykład "rozjazdu" kiedy system pracuję z bazą A a na testy podpinamy bazę B.<br /></li><br /></ul><br /></li></ul>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-86261590751956457752009-06-06T02:23:00.000-07:002013-10-22T13:40:20.944-07:00java encodingWpis został stworzony na podstawie prezentacji Dawida Weissa "The beauty of debugging".<br />
<ul>
<li>Kodowanie plików źródłowych.<br /><span style="font-style: italic;">javac</span> zamienia kod źródłowy na <span style="font-style: italic;">bytecode</span> rozumiany przez JVM. W ten sposób z pliku .java powstaje nam plik .class. W ramach tego procesu literały zawarte w pliku źródłowym trafiają do pliku wynikowego, a dokładnie do <span style="font-style: italic;"> constant pool table</span> i zostają zakodowane za pomocą UTF-8 (<a href="http://en.wikipedia.org/wiki/Class_%28file_format%29">nie jest to 100% prawdą ale możemy tak przyjąć</a>).<span style="font-style: italic;"> javac</span> domyślnie zakłada, że nasz plik źródłowy ma kodowanie zgodne z domyślnym kodowaniem platformy - czyli na moim Windows jest to cp1250. Jeśli mamy w takim razie plik źródłowy a w nim następujący kod:<br /><pre class="brush: java">String str="żółć";</pre>
<br />to w przypadku gdy kodowanie tego pliku jest cp1250 (czyli zgodne z domyślnym kodowaniem platformy)to podglądając ten plik w edytorze hex<span style="font-style: italic;">, </span><span style="font-style: italic;">żółć</span> to następujący łańcuch bajtów (hex): BF F3 B3 E6. Kompilacji pliku źródłowego, tworzy plik .class, w którym to łańcuch<span style="font-style: italic;"> żółć</span> zostaje zakodowany do postaci UTF-8(hex) : C5BC C3B3 C582 C487. Wszystko super, ale gdy nasz plik źródłowy zostanie zakodowany w UTF8 i skompilujemy go tak jak poprzednio, plik wynikowy będzie tym razem zawierał "bezsensowne krzaki". W pliku źródłowym <span style="font-style: italic;">żółć</span> zostanie zakodowana w UTF8 jako:C5BC C3B3 C582 C487, ale <span style="font-style: italic;">javac </span>domyślnie potraktuje ten plik jako zakodowany w Cp1250 i zostanie przeprowadzona konwersja literałów do UTF8. W ten sposób <span style="font-style: italic;">żółć</span> zakodowana w UTF8 zostaje potraktowana jako ciąg bajtów w cp1250, dla którego będzie robiona konwersja do UTF8. W wyniku tego plik wynikowy zakoduje <span style="font-style: italic;">żółć</span> jako następujący ciąg bajtów(hex):C4B9 C4BD C482 C582 C4B9 E280 9AC3 84E2 80A1. Mechanizm jest następujący: C4B9 w UTF8 jest to kod znaku, który odpowiada znakowi C5 w cp1250, C4BD w UTF8 odpowiada BC z cp120 itd. Trzeba pamiętać aby przy kompilacji wskazać kodowanie plików źródłowych: <span style="font-weight: bold;">javac -encoding <span style="font-style: italic;"><kodowanie></kodowanie></span> ...</span> , ponieważ nie zawsze musi być/jest zgodne z kodowaniem domyślnym platformy.</li>
<li>Kodowanie znaków dla operacji I/O<br />W Java typ <span style="font-style: italic;">char</span> jest reprezentowany jako znak Unicode (<span style="font-style: italic;">Unicode code point</span>). Nie oznacza to jednak, że java działa tylko na systemach z <span style="font-style: italic;">charset</span> Unicode. Java domyślnie dokonuje konwersji do i z Unicode, ale ta domyślna konwersja zadziała poprawnie gdy to co chcemy skonwertować lub to, na co chcemy skonwertować jest kodowane za pomocą domyślnego kodowania znaków na naszym systemie. W Java API jest wiele fragmentów, których działanie jest uzależnione od domyślnego kodowania, np. używanie<span style="font-style: italic;"> FileWriter</span>, <span style="font-style: italic;">FileReader</span>, wywołanie <span style="font-style: italic;">getBytes()</span> na obiekcie typu <span style="font-style: italic;">String</span>. Oznacza to, że używanie tych konstrukcji może zupełnie inaczej się zachowywać w zależności od systemu, na którym uruchomimy aplikacje. Następujący kod:<br /><pre class="brush: java">String str = "żółć";
FileWriter fileWriter = new FileWriter("plik.txt");
fileWriter.write(str);
fileWriter.flush();
fileWriter.close);</pre>
<br />zapisuje do pliku plik.txt tekst "żółć". Uruchamiam aplikacje pod Windows i za pomocą edytora hex oglądam zawartość pliku plik.txt:AF F3 B3 E6. Domyślne kodowanie na moim komputerze to Cp1250. Można to sprawdzić odpytując <span style="font-style: italic;">system property</span>: <span style="font-style: italic;">file.encoding</span>. W przypadku uruchomienia aplikacji na Linux (tam file.encoding to UTF-8) plik plik.txt zawiera tekst "żółć", ale zakodowany w UTF8 czyli w edytorze hex zobaczymy następujący łańcuch bajtów: C5 BC C3 B3 C5 82 C4 87. Jak widać wynik działania zależy od platformy, na której uruchomimy aplikacje ...Przy uruchomieniu aplikacji można wymusić domyślne kodowanie znaków za pomocą <span style="font-weight: bold;">-Dfile.encoding=<span style="font-style: italic;"><kodowanie></kodowanie></span></span>, lub też (co wydaje się lepsze) unikać korzystania z elementów API, które wykorzystują domyślne kodowanie</li>
</ul>
W przypadku gdy będziemy chcieli "wypchnąć" znak Unicode, dla którego nie istnieje reprezentacja w kodowaniu, w którym będziemy chcieli ten znak reprezentować, to zobaczymy znak '?'.<br />
<pre class="brush: java">char s = '\u01fe';
FileWriter fileWriter = new FileWriter("foo.bar");
fileWriter.write(s);
fileWriter.flush();
fileWriter.close();
System.out.println(s);</pre>
<br />
Uruchomienie powyższego kodu na moim komputerze z Windows (bez dodawania -Dfile.encoding) skutkuje utworzeniem pliku foo.bar oraz wypisaniem znaku na konsole.<br />
Z racji tego, że znak 'Ǿ' nie jest w żaden sposób reprezentowany w moim domyślnym kodowaniu(cp1250) na konsoli oraz w pliku znajdzie się znak '?' (w hex 3f).<br />
W przypadku gdy przy uruchomieniu aplikacji dodam<span style="font-style: italic;"> -Dfile.encoding=UTF8</span> w pliku zobaczę poprawną reprezentację znaku w kodowaniu UTF8: C7 BE , a na konoli 2 znaczki: Çľ, czyli reprezentaja znaków o kodach odpowiednio C7 oraz BE w kodowaniu cp1250.<br />
Więcej na ten temat <a href="http://www.joelonsoftware.com/articles/Unicode.html">tu</a><br />
<br />
W przypadku gdy potrzebujemy sprawdzić dany znak, szczególnie dotyczy to znaków "wklejonych z nieznanych źródeł" najlepiej wyświetlić każdy znak jako<i> code point</i> oraz jego reprezentacje w wybranym kodowaniu, np UTF-8.<br />
<pre class="brush: java">
int i = 8211;
printRepresentation(i);
char c='ą'
printRepresentation(c);
char c2 = '\u1FB4';
printRepresentation(c2);
private void printRepresentation(int i) throws UnsupportedEncodingException {
System.out.println((char)i + " = " +i +" " + unicodeCodePoint(i) + " '" + utf8HexRepresentation((char)i)+"'");
}
private String unicodeCodePoint(int i) {
return String.format("U+%04X", i);
}
public static String utf8HexRepresentation(char c) throws UnsupportedEncodingException {
byte[] bytes = (""+c).getBytes("UTF-8");
return hexString(bytes);
}
private static String hexString(byte[] data) {
StringBuffer s = new StringBuffer();
for (int i = 0; i < data.length; i++) {
int high_nibble = (data[i] & 0xf0) >>> 4;
int low_nibble = (data[i] & 0x0f);
s.append(hex_table[high_nibble]);
s.append(hex_table[low_nibble]);
s.append(" ");
}
return s.toString().trim();
}
private static char[] hex_table = { '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
</pre>
Wyszukanie "problematycznego" znaku w pliku/plikach można zrobić za pomocą <i>grepa:</i><br />
<pre class="brush: java">
grep --colour $'\xe2\x80\x93' *
</pre>
<pre class="brush: java">
</pre>
<pre class="brush: java">Jeśli chodzi o kodowanie znaków w J2EE to warto zajrzeć <a href="http://balusc.blogspot.de/2009/05/unicode-how-to-get-characters-right.html">tu</a></pre>
milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-30130664936947517302009-06-02T02:03:00.000-07:002009-07-21T06:50:44.388-07:00HamCrest 1.2Nie dawno pojawił się <a href="http://code.google.com/p/hamcrest/">Hamcrest 1.2</a>. Bardzo mi się podoba idea pisania <span style="FONT-STYLE: italic">assercji</span> przy wykorzystaniu <pre class="brush: java">Assert.assertThat(T arg0, Matcher<T> arg1)<br /></pre><br />Pozwala to nie tylko poprawić czytelność kodu, ale także go znacznie skrócić.<br />Na stronie domowej projektu jest parę przykładów użycia, ale lista ta jest trochę uboga i najlepiej samemu zacząć zabawę.<br /><br />Sama konfiguracja pod Eclipse IDE zapowiadała się dość banalnie: wrzucamy hamcrest-all-1.2.jar do <span style="FONT-STYLE: italic">classpath</span> i wszystko. Jednak czekała na mnie niemiła niespodzianka i zobaczyłem:<span style="FONT-STYLE: italic">java.lang.NoSuchMethodError</span>. Okazuję się, jednak że Junit 4.4 (ten co działa z Spring TestContext Framework) ma sam w sobie okrojonego hamcresta...<br />Istenieje na szczęście "goły" JUnit4.4: junit-dep-4.4.jar. W takim razie usunąłem z <span style="FONT-STYLE: italic">classpath </span>junit-4.4.jar a na jego miejsce podgrałem junit-dep-4.4.jar.<br />Od tego momentu pod Eclipse wszystko działa jak należy.<br /><br />Jednak próba uruchomienia, a nawet kompilacji testów z wyrażeniami <span style="FONT-STYLE: italic">hamcrest</span> nie działa.<br />Problem dotyczy <span style="FONT-STYLE: italic">Java generics</span> i pojawiał się już <a href="http://weblogs.java.net/blog/johnsmart/archive/2008/04/on_the_subtle_u.html">wcześniej</a>.<br />Następujący kod kompiluje się w IDE (jdk1.5.0_12)<br /><pre class="brush: java">Assert.assertThat(4, either(equalTo(4)).or(equalTo(3)));<br /></pre><br />Jednak odpalenie <span style="FONT-STYLE: italic">javac</span> (uruchamiane z pełnej ścieżki do jdk1.5.0_12), wywala następujący błąd:<br /><pre class="brush: java">or(org.hamcrest.Matcher) in org.hamcrest.core.CombinableMatcher<java.lang.Object> cannot be applied to (org.hamcrest.Matcher<java.lang.Integer>)<br /> Assert.assertThat(4, either(equalTo(4)).or(equalTo(3)));<br /></pre><br />Zgłosiłem <a href="http://code.google.com/p/hamcrest/issues/detail?id=82"><span style="FONT-STYLE: italic">issue</span></a>, ale odpowiedz jest dość standardowa ze strony twórców projektu: "<span style="FONT-STYLE: italic">flaky Java generics</span>". W takim razie pozostaję nam jedynie znaleźć <span style="FONT-STYLE: italic">workaround </span>i pomóc kompilatorowi...<br /><pre class="brush: java">Assert.assertThat(4, Matchers.<Integer>either(equalTo(4)).or(equalTo(3)));</pre><br /><br />No i miało być tak pięknie, ale zaczęło się sypać uruchamianie testów z <span style="FONT-STYLE: italic">mavena</span>.<br />Po wywaleniu junit-4.4 i dodaniu zależności do junit4.4-dep oraz hamcrest 1.2:<br /><pre class="brush: xml"><dependency><br /> <groupid>junit</groupid><br /> <artifactid>junit-dep</artifactid><br /> <version>4.4</version><br /> <optional>true</optional><br /> <scope>test</scope><br /> <exclusions><br /> <exclusion><br /> <groupid>org.hamcrest</groupid><br /> <artifactid>hamcrest-core</artifactid><br /> </exclusion><br /> </exclusions><br /> </dependency><br /> <dependency><br /> <groupid>org.hamcrest</groupid><br /> <artifactid>hamcrest-all</artifactid><br /> <version>1.2</version><br /> <scope>test</scope><br /> </dependency><br /></pre><br /><span style="FONT-STYLE: italic">SureFire </span>uruchamiał testy nie jako testy <span style="FONT-STYLE: italic">JUnit4 </span>a jako zwykłe <span style="FONT-STYLE: italic">POJO-tests</span>.<br />Niestety dość długo zajęło znalezienie przyczyny, i nie obyło się <a href="http://maven.apache.org/plugins/maven-surefire-plugin/examples/debugging.html">bez</a>. Na szczęście<br />szybciej poszło znalezienie <a href="http://jira.codehaus.org/browse/SUREFIRE-519">rozwiązania<br /></a>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-15521104670485184972009-05-12T11:30:00.001-07:002009-07-21T10:10:22.156-07:00Hibernate i Spring DM ServerSpring Dm Server w wersji 1.0 odpaliłem już prawie rok temu. Udało mi się odpalić aplikację web, która pod spodem korzysta z <span style="FONT-STYLE: italic">Hibernate</span>. Nie ukrywam, że nie poszło to gładko, ale w końcu się udało. Niestety rok po tym, chciałem pobawić się jeszcze raz, tym razem pod STS 2.0, cała wiedza wyparowała i musiałem jeszcze raz się przebijac i oglądać wyjątki w stylu <span style="FONT-STYLE: italic">java.lang.NoClassDefFoundError</span>. W końcu zdecydowałem się to spisać, aby w przyszłości zaoszczędzic sobie ponownej straty czasu.<br />Aplikacja składa się z następujących projektów:<br /><ul><li>web - <span style="FONT-STYLE: italic">Spring DM web bundle</span></li><li>domain - obiekty domeny wykorzystywane przez moduł web oraz moduł api, korzysta z hsqlbd oraz commons dbcp<br /></li><li>api - definiuje interfejs- kontrakt między warstwą web a warstwą dao<br /></li><li>dao - implementuję interfejs zdefiniowany przez moduł api, dostarcza implementacji usługi OSGI</li></ul><br /><ol><li>No PAR deployment- aplikacja składa się z luźno wrzucanych <span style="FONT-STYLE: italic">bundli</span>.<br /><ul><li>konfiguracja ORM określona za pomocą annotacji JPA (AnnotationSessionFactoryBean): <ul><li>dao<pre class="brush: java">Manifest-Version: 1.0<br />Bundle-Version: 1.0.0<br />Bundle-Name: HibernateTest_dao Bundle<br />Bundle-ManifestVersion: 2<br />Bundle-SymbolicName: foo.bar.HibernateTest_dao<br />Import-Package: foo.bar.dao.api,<br />foo.bar.domain,<br />javax.sql;version="[0.0.0,0.0.0]"<br />Import-Bundle: com.springsource.org.apache.commons.dbcp,<br />com.springsource.org.hsqldb,<br />com.springsource.org.hibernate;version="[3.2.6.ga,3.2.6.ga]"<br />Import-Library: org.springframework.spring.instrumented<br /></pre>jak się można było spodziewać, importujemy <span style="FONT-STYLE: italic">bundle </span>Hibernate</li><br /><li>domain<pre class="brush: java">Manifest-Version: 1.0<br />Bundle-Version: 1.0.0<br />Bundle-Name: HibernateTest_domain Bundle<br />Bundle-ManifestVersion: 2<br />Bundle-SymbolicName: foo.bar.HibernateTest_domain<br />Export-Package: foo.bar.domain;version="1.0.0"<br />Import-Bundle: com.springsource.javax.persistence;version="[1.0.0,1.0.0]",<br />com.springsource.org.hibernate;version="[3.2.6.ga,3.2.6.ga]"<br /></pre><br />pomimo, że nie używamy annotacji Hibernate (a jedynie JPA) <span style="FONT-STYLE: italic">bundle</span> Hibernate musi zostac zaimportowany - w przeciwnym przypadku przy starcie <span style="FONT-STYLE: italic">bundle</span> dao dostaniemy wyjątek: <span style="FONT-STYLE: italic">java.lang.NoClassDefFoundError: org/hibernate/proxy/HibernateProxy</span><br /></li></ul><br /></li><li>konfiguracja ORM określona w plikach konfiguracyjnych xml (LocalSessionFactoryBean):<br /><ul><li>dao<pre class="brush: java">Manifest-Version: 1.0<br />Bundle-Version: 1.0.0<br />Bundle-Name: HibernateTest_dao Bundle<br />Bundle-ManifestVersion: 2<br />Bundle-SymbolicName: foo.bar.HibernateTest_dao<br />Import-Package: foo.bar.dao.api,<br />foo.bar.domain,<br />javax.sql;version="[0.0.0,0.0.0]"<br />Import-Bundle: com.springsource.org.apache.commons.dbcp,<br />com.springsource.org.hsqldb,<br />com.springsource.org.hibernate;version="[3.2.6.ga,3.2.6.ga]"<br />Import-Library: org.springframework.spring.instrumented<br /></pre>bez zmian</li><li>domain<pre class="brush: java">Manifest-Version: 1.0<br />Bundle-Version: 1.0.0<br />Bundle-Name: HibernateTest_domain Bundle<br />Bundle-ManifestVersion: 2<br />Bundle-SymbolicName: foo.bar.HibernateTest_domain<br />Export-Package: foo.bar.domain;version="1.0.0"<br /></pre>tym razem <span style="FONT-STYLE: italic">bundle </span>domain nie potrzebuje żadnego importu: ani JPA ani Hibernate</li></ul></li><br /></ul>Aby moc korzystać z <span style="FONT-STYLE: italic">HQL </span>to dla <span style="FONT-STYLE: italic">No-PAR deployment</span> wymagane jest zaimportowanie Hibernate dla <span style="FONT-STYLE: italic">bundle</span> web.<br /><pre class="brush: java">Manifest-Version: 1.0<br />Web-DispatcherServletUrlPatterns: *.html<br />Module-Type: Web<br />Bundle-Version: 1.0.0<br />Bundle-Name: HibernateTest_web Bundle<br />Bundle-ManifestVersion: 2<br />Web-ContextPath: HibernateTest<br />Bundle-SymbolicName: foo.bar.HibernateTest_web<br />Import-Package: foo.bar.dao.api,<br />foo.bar.domain;version="[1.0.0,1.0.0]"<br />Import-Bundle: com.springsource.org.apache.taglibs.standard;version="[1.1.2,1.1.2]",<br />com.springsource.org.hibernate;version="[3.2.6.ga,3.2.6.ga]"<br />Import-Library: org.springframework.spring.instrumented<br /></pre><br />W innym przypadku, przy próbie wykonania zapytania HQL zobaczymy:<span style="font-family:monospace;"> org.hibernate.QueryException: ClassNotFoundException: org.hibernate.hql.ast.HqlToken</span>. Hibernate ładuję cześć klas za pomocą <em>Thread context classloadera</em>, czyli w naszym przypadku za pomocą <span style="FONT-STYLE: italic">ClassLoadera</span>, który załadował moduł web. Najciekawsze jest to, że zapytania korzystające z <span style="FONT-STYLE: italic">Criteria API</span>, operacje <span style="FONT-STYLE: italic">get/load</span> działają bez tego importu. Jest on jedna wymagany do odpalania zapytań w HQL.<br /></li><br /><li>PAR deployment - nasze <span style="FONT-STYLE: italic">bundle</span> zostają zapakowane w archiwum PAR<br /><ul><li>konfiguracja ORM określona za pomocą annotacji JPA (AnnotationSessionFactoryBean): <ul><li>dao<pre class="brush: java">Manifest-Version: 1.0<br />Bundle-Version: 1.0.0<br />Bundle-Name: HibernateTest_dao Bundle<br />Bundle-ManifestVersion: 2<br />Bundle-SymbolicName: foo.bar.HibernateTest_dao<br />Import-Package: foo.bar.dao.api,<br />foo.bar.domain,<br />javax.sql;version="[0.0.0,0.0.0]"<br />Import-Bundle: com.springsource.org.apache.commons.dbcp,<br />com.springsource.org.hsqldb,<br />com.springsource.org.hibernate;version="[3.2.6.ga,3.2.6.ga]";import-scope:=application<br />Import-Library: org.springframework.spring.instrumented<br /></pre>tym razem importujemy <span style="FONT-STYLE: italic">bundle </span>Hibernate z dodatkowym atrybutem <span style="FONT-WEIGHT: bold; FONT-STYLE: italic">import-scope:=application</span>, który jest pewnym rozszerzeniem Spring DM Server<br /></li><br /><li>domain<pre class="brush: java">Manifest-Version: 1.0<br />Bundle-Version: 1.0.0<br />Bundle-Name: HibernateTest_domain Bundle<br />Bundle-ManifestVersion: 2<br />Bundle-SymbolicName: foo.bar.HibernateTest_domain<br />Export-Package: foo.bar.domain;version="1.0.0"<br />Import-Bundle: com.springsource.javax.persistence;version="[1.0.0,1.0.0]"<br /></pre><br />używamy jedynie annotacji JPA, nie musimy importować <span style="FONT-STYLE: italic">bundle</span> Hibernate<br /></li></ul><br /></li><li>konfiguracja ORM określona w plikach konfiguracyjnych xml (LocalSessionFactoryBean):<br /><ul><li>dao<pre class="brush: java">Manifest-Version: 1.0<br />Bundle-Version: 1.0.0<br />Bundle-Name: HibernateTest_dao Bundle<br />Bundle-ManifestVersion: 2<br />Bundle-SymbolicName: foo.bar.HibernateTest_dao<br />Import-Package: foo.bar.dao.api,<br />foo.bar.domain,<br />javax.sql;version="[0.0.0,0.0.0]"<br />Import-Bundle: com.springsource.org.apache.commons.dbcp,<br />com.springsource.org.hsqldb,<br />com.springsource.org.hibernate;version="[3.2.6.ga,3.2.6.ga]";import-scope:=application<br />Import-Library: org.springframework.spring.instrumented<br /></pre>bez zmian</li><li>domain<pre class="brush: java"><br />Manifest-Version: 1.0<br />Bundle-Version: 1.0.0<br />Bundle-Name: HibernateTest_domain Bundle<br />Bundle-ManifestVersion: 2<br />Bundle-SymbolicName: foo.bar.HibernateTest_domain<br />Export-Package: foo.bar.domain;version="1.0.0"<br /></pre>skoro tym razem nie używamy JPA to nie potrzebujemy importu <span style="FONT-STYLE: italic">bundle </span>JPA</li></ul></li><br /></ul>Tym razem do <span style="FONT-STYLE: italic">bundle </span>web nie musimy importować <span style="FONT-STYLE: italic">bundle</span> Hibernate aby móc korzystać z <span style="FONT-STYLE: italic">HQL </span><br /><pre class="brush: java"><br />Manifest-Version: 1.0<br />Web-DispatcherServletUrlPatterns: *.html<br />Module-Type: Web<br />Bundle-Version: 1.0.0<br />Bundle-Name: HibernateTest_web Bundle<br />Bundle-ManifestVersion: 2<br />Web-ContextPath: HibernateTest<br />Bundle-SymbolicName: foo.bar.HibernateTest_web<br />Import-Package: foo.bar.dao.api,<br />foo.bar.domain;version="[1.0.0,1.0.0]"<br />Import-Bundle: com.springsource.org.apache.taglibs.standard;version="[1.1.2,1.1.2]"<br />Import-Library: org.springframework.spring.instrumented<br /></pre><br />Dzięki importowaniu <span style="FONT-STYLE: italic">bundle</span> Hibernate z atrybutem <span style="FONT-STYLE: italic">import-scope:=application</span> nie ma potrzeby importowania Hibernate ani w <span style="FONT-STYLE: italic">bundle </span>web ani w <span style="FONT-STYLE: italic">bundle </span>domain. Po podłączeniu się za pomocą telnet do konsoli Equinox możemy wyświetlić nagłówki <span style="FONT-STYLE: italic">bundli</span> wchodzące w skład naszego PAR, w ten sposób zobaczymy że, każdy z nich "automagicznie" importuje pakiety Hibernate.<br /></li></ol>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-58189892849081113042009-05-12T01:37:00.000-07:002009-05-13T00:06:45.553-07:00GeeCon dzień 2<span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">Java FX For Developers Not Designers<br /></span></span>Poprzedniego wieczora staraliśmy się trochę zintegrować i chyba dlatego prezentacja Adam Biena nie zbyt zapadła mi w pamięć. Z tego co pamiętam to zaczęło się od omówień zmian w JRE: JRE kernel, lepsza komunikacja na linii JavaScript-Applet, możliwość wyspecyfikowania per-applet wymagań co do pamięci, specyfikowanie per-applet wymagań co do wersji JRE. Następnie Adam przeszedł do JavaFX. Było to mniej więcej wszystko to samo co na pozostałych prezentacjach na ten temat. Jedyne co mnie "obudziło" to pisanie testów JUnit w JavaFX. Pojawiło się pytanie czy można to robić w stylu JUnit 4.X, czyli za pomocą annotacji. Odpowiedz było prosta: można użyć JUnit w wersji 4.X, ale nie można korzystać z annotacji, ponieważ JavaFX nie wspiera annotacji!.<br /><br /><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">RESTful Web Services with Spring 3.0</span></span><br />Znużony trochę prezentacją na temat JavaFX na drugie pół godziny przeniosłem się do sali obok aby posłuchać Arjena Poutsmy. Było to praktycznie to samo co <a href="http://jmilkiewicz.blogspot.com/2009/03/spring-30-talk-with-arjen-poutsm.html">Arjen zaprezentował jakiś czas temu w Warszawie</a>. Arjen oprócz tego, starał się wytłumaczyć czym <span style="font-style: italic;">Spring Framework REST support </span>różni się od implementacji JAX-RS: ten pierwszy dotyczy interfejsu <span style="font-style: italic;">human to machine</span> a ten drugi <span style="font-style: italic;">machine to machine</span>. Fuknkcjonalności w stylu: filtr, który obchodzi ograniczenie przeglądarki do wysyłania jesdynie żądan POST i GET, używanie <span style="font-style: italic;">file extensions</span> w celu określenia żądanej reprezentacji (kolejne obejście przeglądarki) mają ułatwić tworzenie aplikacji zgodnych z paradygmatem REST. Jednak jak to później Arjen w rozmowie uściślił @MVC/@REST ma duże zastosowanie dla <span style="font-style: italic;">web development</span>, a JAX-RS dla <span style="font-style: italic;">RESTful services, </span>czyli ma stanowić konkurencje dla <span style="font-style: italic;">SOAP WebServices.</span> Arjen wspomniał także o <span style="font-style: italic;">RestTemplate </span>czyli o REST po stronie klienta. Na koniec pojawiło się pytanie związane z <span style="font-style: italic;">security </span>w świecie REST. Po stronie serwera nic się nie zmienia i standardowo używać <span style="font-style: italic;">form-based authentication</span> oraz SpringSecurity(jest w stanie sprawdzać prawo do wywołania zasobu także na podstawie użytej metody HTTP), a dla <span style="font-style: italic;">RestTemplate </span>używać <span style="font-style: italic;">basic/digest authentication</span> i przesyłać nasze <span style="font-style: italic;">credentials </span>przy każdym żądaniu.<br /><br /><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">Glassfish</span></span><br />Dodatkowa, nieplanowana prezentacja przeprowadzona przez Antonio Goncalves na temat GlassFish. Antonio zaprezentował jak zainstalować, skonfigurować i używać GlassFish. Wyglądało to jako typowy <span style="font-style: italic;">step-by-step turtorial</span> - od pobierania instalki poprzez uruchamiania serwera i jego konfiguracje każdy etap został zaprezentowany z uprzednio przygotowanych filmików flashowych- nic nie miało prawo nie działać :). Filmiki zostały stworzone przez Antonio na podstawie kroków opisanych w <span style="font-style: italic;">GlassFish Quick Start Guides</span>.<br />Serwer można zainstalować w 3 trybach: "development", "cluster", ""enterprise". Nie obyło się bez pokazania 2 alternatywnych interfejsów dostępu do serwera: <span style="font-style: italic;">Admin Console</span> i <span style="font-style: italic;">asadmin</span> oraz narzędzie (stworzone w <span style="font-style: italic;">swingu</span>) do instalowania/aktualizowania komponentów samego serwera <span style="font-style: italic;">Update Center</span>. Bardzo mi się spodobało, że Antionio miał przygotowane slajdy omawiające architekturę samego serwera i na tej podstawie wyjaśniał poszczególne pojęcia: <span style="font-style: italic;">domain, administration server, node agent, cental repository<span style="font-style: italic;">, cluster</span>, server instance</span>. Na końcu jeszcze krótka informacja na temat Glassfish V3 , jako referencyjnej implementacji JEE6, z modularną architekturą opartą na OSGI. Rozmawiając na temat OSGI w kontekście V3 z Antionio, usłyszałem, że jak na razie nie ma żadnych planów aby serwer mógł hostować aplikację użytkownika oparte na OSGI.<br /><br /><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">What's new in Java EE 6</span></span><br />Antonio Goncalves tym razem opowiadał na temat JEE6. Same wydanie specyfikacji jest dość mocno opóźnione z tym co pierwotnie zakładano, a dodatkowo wokół paru specyfikacji zrobiło się dość dużo szumu i wiele emocji :). W JEE 6 pewne specyfikacje zostały "wycięte" m.in.: JAX-RPC, EJB 2.1 entity beans, JSR 88, JAXR. Wprowadzono w końcu pojęcie <span style="font-style: italic;">profili</span>, czyli JEE ma przestać być jednym wielkim monolitycznym kolosem w stylu <span style="font-style: italic;">one size fits all</span>. Aktualnie w przygotowaniu są 3 profile, przy czym jakie specyfikacje będą należeć do jakiego profilu jeszcze nie jest w 100 % pewne.<br />JEE6 wprowadza wiele zmian i to, w każdej warstwie JEE:<br /><ul><li>Servlets 3.0 (annotacje, <span style="font-style: italic;">fragments</span>, możliwośc rejestrowania/odrejestrowywania servletów, przetwarzanie asynchroniczne), JSF 2.0, AJAX support, JSP jako technologia niszowa</li><li>JAX-WS 2.2, JAX-RS(nie wspiera <span style="font-style: italic;">client-side REST</span>)</li><li>EJB 3.1(optional local intefaces), @Singleton (per application per JVM), @ConcurrencyManagement, EJB Lite, <span style="font-style: italic;">EJB asynchronous methods</span> ( zwracją <span style="font-style: italic;">null </span>lub <span style="font-style: italic;">Future</span>, domyślnie dla nich <span style="font-style: italic;">Propagation.Requires_NEW</span>), możliwość pakowania EJB w WAR, globalne ( przenośne) nazwy JNDI, Timer Services API( prawie jak <span style="font-style: italic;">Cron</span>), Embeddable EJB Container<br /></li><li>JPA 2.0 (m.in. mapowanie kolekcji komponentów, rozbudowa JPQL, dodatnie Criteria API, nowe mechanizmy <span style="font-style: italic;">lockowania</span>)<br /></li><li>Web Beans przemianowane na JDCI</li></ul>Tak przygotowana specyfikacja ma nawet umożliwiać tworzenie aplikacji <span style="font-style: italic;">batchowych</span>:<br />Batch processing = EJB Lite + Timer + Asynch calls + Embeddable Container<br /><br /><br /><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">Automation of functional tests</span></span><br />Tomasza Kaczanowskiego w swojej prezentacji przedstawił nam idee automatyzacji testów funkcjonalnych. W przypadku testów funkcjonalnych pojawia się zawsze problem związany z przygotowaniem środowiska uruchomieniowego dla testów, które powinno jak najbardziej przypominać środowisko produkcyjne. Przygotowanie takiego środowiska oraz mechanizmu automatyzacji testów funkcjonalnych nie zawsze jest łatwe/możliwe do osiągnięcia i często trzeba do tego podejśc pragmatycznie. Tomasz na podstawie własnych doświadczeń przedstawił w jaki sposób przeprowadził automatyczne funkcjonalne testowanie 2 odmiennych systemów: klasyczny system JEE z interfejsem web, system oparty na OSGI z interfejsem SOAP WebSerices. Przygotowanie środowiska dla testów funkcjonalnych zostało rozbite na poszczególne fazy:<br /><ul><li>stworzenie bazy danych -przykładowe narzędzia: hibernate3-maven-plugin, SQL-maven-plugin</li><li>załadowanie danych - użycie DBUnit (podobno DBUnit ma problemy z ładowaniem duzej ilości kodu napisanego w PL/SQL), SQL-maven-plugin</li><li>wrzucenia aplikacji - cargo-maven-plugin, capistarano ?</li><li>uruchomienie testów - selenium-maven-plugin, soapui-maven-plugin</li></ul>Tomasz ostrzegał, że w/w narzędzia bardzo pomogły mu osiągnąc zamierzony efekt, ale na pewno nie są one "<span style="font-style: italic;">silver bullet</span>" i nie koniecznie muszą działac w innych środowiskach.<br /><br /><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">SOLID design principles</span></span><br />Super zabawna prezentacja przeprowadzona w typowym włoskim stylu. Prezentacja bazuje na<br /><span style="font-style: italic;">Object Oriented Design principles</span> zdefiniowanych przez wujka Boba czyli Boba Martina.<br />Bruno Bussola zaczął od omówienia czemu powinno nam zależeć na posiadaniu "good design" oraz jakie są symptomy, że coś jest nie tak z naszym <span style="font-style: italic;">designem</span>:<br /><ul><li>rigidity - wprowadzenie zmiany jest nieprzewidywalne(bardzo trudne/nie możliwe do oszacowania pod względem czasu i kosztu). Nawet mała zmiana powoduje, że musimy zmieniać coraz to kolejne elementy (<span style="font-style: italic;">change marathon</span>)</li><li>fragility - wprowadzenie zmiany w jednym elemencie powoduje, że zaczyna "się sypać" w innym teoretycznie nie powiązanym fragmencie</li><li>immobility - poszczególne elementy są ze sobą tak powiązane, że nie ma szans aby "wyjąc" pojedyńczy element i re-użyć go.</li><li>viscosity - przy wprowadzeniu zmiany o wiele łatwiej jest napisać "hack"niż zrobić to poprawnie - zgodnie z <span style="font-style: italic;">designem</span></li></ul>Jak dobrze wiemy dobry <span style="font-style: italic;">design</span> powinien się cechować przede wszystkim 2 rzeczami: <span style="font-style: italic;">low coupling, high cohession</span>. Oczywiście łatwiej o tym mówić niż zrobić, ale wujek Bob przygotował nam zbiór zasad aby osiągnąć zamierzony cel. SOLID jest to akronim przy czym każda z liter jest pierwszą literą kolejnych akronimów:<br /><ul><li>Single Responsibility Principle - klasa powinna odpowiadać za tylko 1 aspekt(posiadać pojedynczą odpowiedzialność), czyli powinien być tylko pojedynczy powód dla którego mielibyśmy wprowadzać zmiany w klasie. Przykładowo, klasa Employ nie powinna jednocześnie posiadać metod związanych z obliczaniem pensji, persystencją i generowaniem raportu na podstawie danych użytkownika, a interfejs Modem nie powinien posiadać metod związanych z obsługa komunikacji(stop/start) i obsługą komunikatów. Dośc łatwo jest znaleźć klasy, które łamią te zasady: są to zazwyczaj te, które pełnią role <span style="font-style: italic;">facady</span>: *Controller, *Manager<br /></li><li>Open-Closed Principle: powinniśmy móc dodawać nową funkcjonalność do naszego kodu bez dokonywania zmiany w istniejącym kodzie. W Javie stosunkowo łatwo się implementuję tę zasadę za pomocą polimorfizmu. Oczywiście trzeba starać się/umieć przewidzieć jakie funkcjonalności mogą/mają szansę się zmienić...<br /></li><li>Liskov Substitution Principle - zasada zastępowania, czyli możliwość przekazania zamiast obiektu danej klasy, obiekt podklasy bez zmieniania żadnych oczekiwanych zachowań. Wszystko to bardzo ładnie przedstawia przykład kwadrat i prostokąt. Czy kwadrat jest prostokątem? Z matematycznego punktu widzenia tak, ale niestety takie myślenie nie jest zgodne z tą zasadą. Prostokąt ma szerokość i wysokość, przy czym obie te wartości są niezależne - możemy zmienić jeden z wymiarów bez zmieniania drugiego z nich. Kwadrat ma tylko 1 wymiar - szerokość, ale skoro widzimy go jako prostokąt to możemy zmieniać jego wysokość i szerokość. Zmiana jakiejkolwiek z tych wartości wpływa bezpośrednio na drugą, czyli łamie kontrakt określony przez prostokąt.</li><li>Interface Segregation Principle - zasada określająca, że powinniśmy używać interfejsów do definiowania zależności pomiędzy 2 elementami kodu. Jeśli klasa wystawia kilka "grup powiązanych metod", w szczególności gdy każda z tych grup jest wykorzystywana przez innego klienta, zależnośći takie powinno się przedstawić w postaci pojedynczego interfejsu dla każdej z "grup metod"(pojedynczy klient zależy od pojedynczego interefejsu, który enkaspuluje powiązane ze soba metody, a nie od interfejsu zawierającego wszystkie możliwe metody)<br /></li><li>Dependecy Inversion Principle - Moduły wysokopoziomowe nie powinny zależeć od modułów niskopoziomowych. Obie grupy modułów powinny zależeć od abstrakcji. Abstrakcje nie powinny zależeć od szczegółowych rozwiązań. To szczegółowe rozwiązania powinny zależeć od abstrakcji.</li></ul><br /><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">Developing RESTful Web services with Jersey<br /></span></span>Prezentacja Jakuba Podlesaka na temat JAX-RS, a w szczególności projektu Jersey. Zaczęło się standardowo od tego czym jest REST: <span style="font-style: italic;">resources</span>, <span style="font-style: italic;">uniform service interface</span>, <span style="font-style: italic;">multiple representations</span>, <span style="font-style: italic;">hypermedia exhchange</span> (<span style="font-style: italic;">boot URI paradigm</span>), <span style="font-style: italic;">stateless</span>. Jersey będący <span style="font-style: italic;">RI </span>JAX-RS może zostać uruchomiony w <span style="font-style: italic;">Java Servlets Container, embedded HTTP Server, JAX-WS Provider.</span> Jesrsey (nie wiem czy jest to w specyfikacji JAX-RS) jest w stanie wygenerować nam WADL, dokument opisujący nasze usługi oraz dodatkowo wprowadza @Consume. Annotacja ta pozwala na <span style="font-style: italic;">bindowanie</span> ciała żądania HTTP do obiektu. Z tego co wiem to jest to coś nad czym aktualnie pracuje Arejn Poustsma w ramach Spring @Rest.<br />Jeresey out-of-box wspiera JAXB, czyli marshalling/unmarshalling w formatach XML oraz JSON jest zapewniony. <span style="font-style: italic;">Content-negotiation</span> jest określany na podstawie nagłówka <span style="font-style: italic;">Accept </span>oraz rozszerzenia zasobu. Aby wysyłać z poziomu przeglądarki żądania HTTP: PUT i DELETE używany był plugin do firefox - Poster.<br />Pomimo tego, że JAX-RS dotyczy jedynie warstwy serwerowej Jersej posiada także API dla klientów usług REST.milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-2564464236264641072009-05-09T06:54:00.000-07:002009-05-28T09:15:06.515-07:00GeeCon dzień 1GeeCon okazał się być największą i najlepszą polską konferencją Javowa na jakiej dotychczas byłem.<br /><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">JavaFX: The Platform for Rich Internet Applications</span></span><br />Keynote Simona Rittera na temat Java FX. Niezbyt interesuję się technologiami związanymi z UI oraz widziałem Java FX w akcji podczas Devoxx i chyba dlatego nie do końca dałem się wciągnąć w prezentacje... Podstawowa informacja to, że Java FX jest językiem skryptowym działającym na JRE , pod spodem korzysta z dobrodziejstwa Javy i służy jako język do tworzenia UI uruchamianych w różnych środowiskach: <em></em> przeglądarka, desktop, urządzenia mobilne, oraz inne wynalazki w stylu TV set top box, konsole gier i inne takie.<br />Siłą tej technologii ma być prostota, czyli szybkość i łatwość tworzenia UI, które mają działać w wielu środowiskach, czyli 1 język do tworzenia GUI na wielu platformach - <span style="font-style: italic;">cross screen functionality</span>.<br />Ze względu na to, że przez jakiś czas pracowałem jako J2me developer bardziej zaintersowało mnie wykorzystanie Fx w świecie telefonów komórkowych. Jestem ciekaw czy jest to technologia, która poradzi sobie z problemem <span style="font-style: italic;">fragmentation</span> w świecie mobile. Jak na razie podobno nie ma urządzenia które wspierałoby JAVA FX <span style="font-style: italic;">out of box</span>, ale Sun ma rozmawiać na ten temat z vendorami. Teoretycznie do uruchamianie aplikacji FX na telefonie potrzebna będzie końcówka z obsługą MIDP 2.0 + parę dodatkowych jarów. FX wprowadza <span style="font-style: italic;">profile API</span>, czyli dodatkowe API związane z renderowaniem UI oraz innymi możliwościami (np. PIM oraz obsługa SMS dla <span style="font-style: italic;">Mobile profile</span>) związanymi z platformą uruchomieniową.<br />W świecie UI (i co za tym idzie RIA) szczególnie ważne jest tworzenie "wodotrysków" i tu Java FX może się na prawdę wykazać. Obsługa audio, video, animacji, <span style="font-style: italic;">rich text</span> jest na prawdę bardzo łatwa, a dodatkowo są też pluginy, które umożliwiają integracje projektów tworzonych w PhotoShop i innych dziwactwach do wykorzystania ich w Java FX. Jako, że aplikacja oprócz UI potrzebuje też trochę logiki, FX posiada wsparcie do komunikacji przez WebServices - chyba chodzi o klasyczne SOAP WebServices (z tego co słyszałem to także Asynchronous Web Services) oraz Restful WebServices.<br /><br /><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">Collaboration, data synchronization and offline capabilities for Rich Internet Applications</span></span><br />Bardzo przyjemna prezentacja na temat Flex. Cornelaiu po omówieniu ogólnej architektury skupił się na zaprezentowaniu 2 aspektów:<span style="font-style: italic;"> data synchronization </span>oraz <span style="font-style: italic;">offline</span>. Dodatkowo zaprezentował <a href="http://www.adobe.com/devnet/flex/tourdeflex/">tour de flex</a> czyli zestaw przykładów-aplikacji dzięki którym można zobaczyć co Flex potrafi. Nie chodzi jedynie o UI, ale także o pewne feature, które jest bardzo ciężko zaimplementować korzystając z "klasycznych technologii webowych". Pokazano typowe przykłady, w których to z poziomu paru okien przeglądarki widać te same dane, a zmiany robione przez użytkownika są jednocześnie propagowane do okien pozostałych użytkowników. W przypadku konfliktów czyli, np. 2 userów jednocześnie robi zmianę tego samego wiersza Flex wykrywa taką sytuację i pozwala nam coś takiego obsłużyć. Z tego co się orientuje jeśli model który oglądamy w UI jest zbindowany do Hibernate to Flex potrafi podpiąc się pod mechanizm <span style="font-style: italic;">optimistic locking</span>, a w innym przypadku jest to zaimplementowane poprzez cachowanie danych zanim zostaną dostarczone klientowi. Dodatkowo mechanizm <span style="font-style: italic;">Data Management</span> umożliwia korzystanie z Hibernate po stronie klienta, tzn. obiekty w ActionScript są zbindowane do obiektów hibernatowych i ich modyfikacja, ale także odczyt asocjacji (<span style="font-style: italic;">lazy associations</span>) jest propagowany na serwer. Dodatkowo Flex obsługuje <span style="font-style: italic;">comet</span> przy czym ma możliwość "dostosowania" się do działającej infrastruktury: próbuje otworzyc dedykowany socket do serwera, a jeśli to się nie uda to wykorzystuję "klasyczne mechanizmy": Long Pooling , Http Streaming. Kolejny aspektem była praca offline. Jeśli nasz klient straci łącznośc z serwerem, możemy cały czas działać - oczywiście do momentu kiedy nie staniemy się znów online, nie widzimy zmian na serewreze ,ale możemy wykonywać pewne operacje i zapisywać je w lokalnym <span style="font-style: italic;">storage </span>. W przypadku aplikacji nie jest uruchamiana w przeglądarce (aplikacja dektopowa) Flex umożliwia nam korzystanie z wbudowanego SQL Lite. Dla Flexa uruchamianego w przeglądarce ten storage jest bardziej ubogi. Został zaprezentowany przykład, w którym to napisany przez nas <span style="font-style: italic;">listener</span> obsługuję logike związana z zapisywaniem i "wypychaniem" danych na serwer jeśli odpowiednio przejdziemy z online od offline i offline do online. Podobno wszystkie przykłady które zostaly zaprezentowane są dostępne na <a href="http://cornelcreanga.com/">blogu</a><br /><br /><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">Liberate your enterprise Java applications from the Operating System (or: about the JVM-level virtualization)</span></span><br />Chyba najciekawsza (przynajmniej dla mnie) prezentacja dnia. Waldek zaczął od omówienie czym jest wirtualizacja i jakie są korzyści z jej stosowania(przykładowo: "be green", lepsze wykorzystanie maszyn, HA, management). Następnie przedstawił 2 ogólne rodzaje infrastruktury wirtualizacji: <span style="font-style: italic;">Host(OS-based)</span> oraz <span style="font-style: italic;">Hypervisor(bare-metal)</span> . W pierwszym przypadku, w istniejącym systemie operacyjnym instalujemy oprogramowanie, które umożliwia nam uruchamianie maszyn wirtualnych. Jest to architektura, w której pracuje VmWare Workstation, VmWare Server czy VirtualBox. W przypadku <span style="font-style: italic;">Hypervisor</span>, oprogramowanie umożliwiające uruchamianie maszyn wirtualnych jest instalowane bezpośrednio na hardware, czyli pomijamy system operacyjny. Tak działa Oravle VM, Vmware ESX/ESXi. Architektura <span style="font-style: italic;">bare-metal</span> ma nie tylko być wydajniejsza , ale także izoluje nas od stabilności(tzn. braku stabilności) systemów operacyjnych, na których instalowane jest oprogramowanie infrastruktury wirtualizacji. Następnie Waldek przedstawił JRockitVE. Produkt najprościej można opisać jako JRockitVE = JRockitVM + "thin kind-of OS layer". Dzięki JRockitVE mamy możliwość bezpośredniego uruchamiania aplikacji Javowych na maszynie wirtualnej bez potrzeby instalowania na niej systemu operacyjnego.<br />JVM do swojej pracy(szczególnie jeśli mamy od czynienia z serwerem aplikacyjnym) potrzebuje jedynie pewnych funkcjonalności systemu operacyjnego, np. obsługa urządzeń, system I/O.<br />Dlatego Jrockit VE dostarcza pewną cienką warstwę, która ma emulować te braki. Waldek podkreślał, że ta warstwa powstała od zera (podejście <span style="font-style: italic;">bottom-up</span>) a nie w wyniku usuwanie zbędnych fragmentów jakiegoś istniejącego OS i oczywiście nie wspiera pewnych funkcjonalności: gui, sound, jni. Z punktu widzenia Javy EE braki te mają być niezauważalne. Taka dostarczana przez Jrockit VE OS jest jedno-procesowy (nie ma możliwości uruchomienia aplikacji/serwera aplikacyjnego oraz bazy danych w ramach jednej maszyny wirtualnej chyba, że baza będzie sama w sobie uruchamiania w JVM) , jedno-użytkownikowy oraz jest w stanie korzystać z funkcji znajdującego się pod spodem <span style="font-style: italic;">hypervisora</span> (<span style="font-style: italic;">paravirtualization</span>). Dodatkowo do tak uruchomionej maszyny wirtualnej możemy się podłączyć za pomocą SSH, jest system plików, jest też jakiś ograniczony shell.<br />Uruchamianie aplikacji bezpośrednio JRockit VE pozwala nam pozbyć się OS i związanego z nim narzutu: OS nie musi zajmować <span style="font-style: italic;">storage</span>, nie zajmuje RAM, nie ma przejść pomiędzy <span style="font-style: italic;">privileged i user mode</span>.<br />Na koniec najfajniejsze były przykłady, w których to Waldek za pomocą dostarczanego z JRockitVE narzędzia tworzył z aplikacji Javowej i pliku konfiguracyjnego obraz maszyny wirtualnej i wrzucał go do VmWare Workstation. Na potrzeby prezentacji nie było to środowisko 1:1 z tym co opisywał wcześniej, ale i tak robiło to duże wrażenie..<br /><br /><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">Making EJB 3.1 development a breeze with Apache OpenEJB</span></span><br />Pierwotnie zająłem miejsce w sali obok i chciałem posłuchać o Scala, jednak gdy na jednym z początkowych slajdów zobaczyłem jakieś krzaki, tzn. jakieś wyrażenie w języku funkcyjnym to czym prędzej uciekłem do sali obok. Przypomniały mi się od razu wyrażenia λ (lambda), które starałem się jakoś pojąć na studiach i jakoś zawsze szło mi to opornie.<br />W 2 sali Jacek Laskowski zaprezentował OpenEJB jako kontener EJB 3.1. Byłem już na kilku prezentacjach, które omawiały JEE 6 i nie były to dla mnie zbytnie nowości. Z 2 strony cały czas mnie ciekawi co Jacek miał na myśli gdy mówił o integracji OpenEJB ze SpringFramework...<br /><br /><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">Practical Groovy</span></span><br />Prezentacja na temat Groovy przeprowadzona przez pracownika JetBrains, czyli wszystkie przykłady tworzone i uruchamiane w IntelliJ IDEA. Prezentacja dość typowa dla tych omawiających Groovy: zaczyna się od pokazania ile trzeba "naklepać" w Javie oraz jak szybko da się to przerobić na Groovy (za pomocą narzędzia i/lub ręcznie) aby finalnie zobaczyć zdecydowanie mniejszą ilość kodu z w pełni zachowaną funkcjonalności. Wszystko to wygląda bardzo ładnie, ale mnie przede wszystkim przeraża brak silnej kontroli typów w Groovy, co zmusza developerów do pisania jeszcze większej liczby testów aby wyłapać takie bugi. Podobno można dodatkowo się w tym aspekcie wesprzeć narzędziami do analizy kodu. Osobiście spodobała mi się idea pisania testów jednostkowych dla kodu w Javie w Groovym. Wyglądało to na prawdę całkiem zwięźle i prosto. Oczywiście nie obyło się od prezentacji pokazującej modyfikowanie kodu w w runtime co dla Javowca jest zawsze dużym zaskoczeniem. Groovy podobno bardzo ładnie nadaje się do tworzenie skryptów Anta, budowania konfiguracji Springowych oraz można go wykorzystywać do tworzenia różnej maści DSL. Dodawanie "w locie" metod do klas, używanie <span style="font-style: italic;">clousures </span>i paru jeszcze innych wynalazków, których nazw nie zapamiętałem na pewno wpłynie pozytywnie na produktywność . Jeszcze tylko nie wiem czy taki kod nie okaże się bardzo "<span style="font-style: italic;">fragile</span>".<br /><br /><span style="font-weight: bold;font-size:130%;" ><span class="lecture_title">Building Flex applications with Java Google App Engine backends</span></span><br />Kolejna prezentacja z Flex w temacie. Zaczęło się niestety od problemów technicznych z projektorem i niestety większość prezentacji była wyświetlana w wściekle różowych kolorach. Z całej prezentacji nie interesował mnie Flex, a Google App Engine. Google dotychczas na swoim App Engine umożliwiało wrzucanie aplikacji napisanych w Python, od niedawna jednak można hostować tam aplikacje Javowe. Z tego co się orientuję to chodzi głównie o aplikacje <span style="font-style: italic;">webowe </span>oparte na Java Servlets(z tego co pamiętam to Servlets 2.4) oraz JSP. Dodatkowo można korzystać z dostępnych usług: <span style="font-style: italic;">storage</span> za dostępem za pomocą JDO/JPA, dostęp do Memcache, Java Mail, Authentication/Authorization (na podstawie google accounts). Google dostarcza Google App Engine Java SDK, który może zostać zainstalowane standalone lub jako plugin do Eclipse.W ten sposób łatwo możemy tworzyć, budować, testować (za pomocą lokalnej instancji Jetty) i uploadowac aplikacje na Google App Engine. Podobno nie ma problemu aby uruchomić w taki sposób aplikacje napisaną w Groovy lub za korzystającą z Spring Framework. Podobno jest także wersja BlazeDS, która ma działać na GAE. Google dostarcza jednocześnie model biznesowy(czytaj <span style="font-style: italic;">pricing</span>) dla swojego produktu, ale podobno całkiem dużo można się pobawić za free. O zabawach z flexem na GAE można znaleźc <a href="http://www.riaspace.net/2009/05/flex-on-google-java-app-engine/">tu</a>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-40328489487059862942009-03-18T07:12:00.000-07:002009-03-18T08:29:25.175-07:00AbstractAdvisorAutoProxyCreatorOstatnio dostałem zadanie dopisania paru rzeczy w projekcie wykorzystującym <span style="font-style: italic;">Spring Framework</span> w wersji 1.2. Dodatkowo chciałem podbić wersję <span style="font-style: italic;">Springa</span> do aktualnie wykorzystywanej w innych projektach, czyli do wersji 2.5.6. Parę razy już taką operację robiłem i zazwyczaj sprowadzało się to do wymiany jarów. Tym razem nie było inaczej i już po chwili aplikacja działała z nowym wersją <span style="font-style: italic;">Springa</span>.<br />W projekcie należało dodać jedną prosty aspekt i chciałem wykorzystać do tego nową (dostępną już w wersji 2.0) składnie definiowania w xml konfiguracji <span style="font-style: italic;">Spring AOP</span>, czyli korzystanie z przestrzeni nazw <span style="font-style: italic;"></span><em>aop</em>.<br />Po wprowadzeniu zmian i przetestowaniu, wyglądało jakby zadanie miało być skończone. Jednak zauważyłem dziwną rzecz: niektóre <span style="font-style: italic;">beany</span>, które dotychczas nie były opakowywane przez żaden z aspektów zostały tym razem opakowane - zamiast <span style="font-style: italic;">POJO</span> były to <span style="font-style: italic;">AOP proxy</span>. Dotyczyło to tylko niektórych beanów, ale co ciekawsze zostały opakowane nie przez mój aspekt, ale przez inny istniejący zanim zacząłem wprowadzać zmiany.<br />Jak usunąłem wprowadzone przeze mnie zmiany wszystko zaczęło działać jak poprzednio i <span style="font-style: italic;">beany</span>, które jeszcze przed chwilą widziałem jako <span style="font-style: italic;">Proxy</span> teraz były zwykłymi <span style="font-style: italic;">POJO</span>.<br />Wyglądało to jakby wprowadzenie nowego aspektu rozwaliło cała dotychczasową konfigurację <span style="font-style: italic;">Spring AOP</span>.<br />Zacząłem przeglądać źródła Springa i po jakimś czasie znalazłem rozwiązanie. Problem wynikał z tego, że dotychczasowa konfiguracja Spring AOP opierała się na tworzeniu <span style="font-style: italic;">ProxyFactoryBean</span> i podpinaniu do nich wszelkiego typu <span style="font-style: italic;">beanów</span> będących <span style="font-style: italic;">Advisorami</span> lub <span style="font-style: italic;">Interceptorami</span>, a użycie nowej konfiguracji xml <span style="font-style: italic;"><aop> </aop></span>powoduję uaktywnienie się <span style="font-style: italic;">beana </span>typu <span style="font-style: italic;">AspectJAwareAdvisorAutoProxyCreator</span>, który to jest podklasą klasy <span style="font-style: italic;">AbstractAdvisorAutoProxyCreator</span>. Klasa ta próbuje z każdego <span style="font-style: italic;">beana</span> (tak nie do końca z każdego, ponieważ nie bierze pod uwagę pewnych klas <span style="font-style: italic;">beanów</span>) stworzyć <span style="font-style: italic;">AOP proxy</span> na podstawie zdefiniowanych w systemie <span style="font-style: italic;">advisorów</span>. W wersji 1.2 aby otworzyć <span style="font-style: italic;">AOP proxy </span>należało do beana typu ProxyFactoryBean podpiąć nazwe <span style="font-style: italic;"></span><span style="font-style: italic;">beana </span><span>będący </span><span style="font-style: italic;">advisorem</span>, to teraz sam Spring dla każdego beana wyszukuje <span style="font-style: italic;">liste advisorów</span>, które do niego "można zastosować".<br />Jeśli lista ta nie będzie pusta, to tworzy dla danego <span style="font-style: italic;">beana AOP proxy</span> i rejestruję w kontekście.<br /><br />Zachowanie takie można zasymulować także w Springu 1.X poprzez dodanie do kontekstu beana typ <span style="font-style: italic;">D</span><span style="font-style: italic;">efaultAdvisorAutoProxyCreator</span>.<br /><br />Wielokrotnie słyszałem o tym aby nie mieszać różnych opcji konfigurowania tych samych rzeczy w <span style="font-style: italic;">Spring</span>: choćby jednoczesnego definiowanie transakcji za pomocą annotacji i konfiguracji w xml. To samo dotyczy zresztą sposobu konfigurowania AOP, czego sam doświadczyłem.milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-51365853288125294782009-03-13T15:46:00.000-07:002009-07-21T10:11:28.637-07:00Spring WebServices on Websphere 6.1.0.xJuż jakiś czas temu stanąłem przed problemem uruchomienia Spring WebServices na Websphere 6.1.0.x. Chodziło o dołożenie do istniejącej aplikacji <span style="font-style: italic;">ear</span> nowego modułu <span style="font-style: italic;">web </span>(modułu webservices), który miał stanowić alternatywny interfejs dostępu do systemu. Oczywiście istniejący oraz nowy moduł <span style="font-style: italic;">web </span>muszą delegować do współdzielonej warstwy <span style="font-style: italic;">service</span>.<br />Sposób zdefiniowania konfiguracji Spring, w której to 2 <span style="font-style: italic;">contexty</span> springowe modułów <span style="font-style: italic;">web</span> aplikacji <span style="font-style: italic;">ear </span>potrafią współdzielić wspólny <span style="font-style: italic;">context</span> jest poza tematem tego wpisu.<br /><br />Ze względu na problemy związane z "Jar Hell" próbując różnych ustawień i kombinacji JARów, przez długi czas przy starcie i/lub obsłudze komunikatów widywałem wyjątki: <span style="font-style: italic;">java.lang.VerifyError</span><span style="font-weight: bold; font-style: italic;">, </span><span style="font-style: italic;">java.lang.ClassCastException, java.lang.NoClassDefFoundException</span>. W końcu udało mi się zestawić konfigurację w której działały <span style="font-style: italic;">enpointy </span>typu <span style="font-style: italic;">PayloadRootAnnotationMethodEndpointMapping</span> wraz z <span style="font-style: italic;">JAXB 2</span>:<br /><ul><li>ze względu na to że mam 2 moduły <span style="font-style: italic;">web, </span>których <span style="font-style: italic;">contexty </span>mają delegować do współdzielonego contextu modułu <span style="font-style: italic;">service</span>, <span style="font-style: italic;">jary </span>spring framework są umiejscowione bezpośrednio pod <span style="font-style: italic;">ear</span>, a moduły <span style="font-style: italic;">web</span> odwołują się do nich za pomocą wpisów we własnych plikach <span style="font-style: italic;">Manifest.mf</span> (sekcja <span style="font-style: italic;">Class-Path</span>)</li><li>W katalogu <span style="font-style: italic;">WEB-INF/lib</span> modułu web services wrzuciłem następujące <span style="font-style: italic;">jary </span><span>(wszystkie</span><span> skopiowane z katalogów <span style="font-style: italic;">dist </span>oraz <span style="font-style: italic;">lib </span>dystrybucji <span style="font-style: italic;">Spring WebServices</span>)</span><span style="font-style: italic;">:</span><ul><li>activation-1.1.1.jar</li><li> jaxb-api-2.1.jar</li><li>jaxb-impl-2.1.5.jar</li><li>spring-oxm-1.5.2.jar</li><li>spring-oxm-tiger-1.5.2.jar</li><li>spring-webmvc.jar</li><li>spring-ws-core-1.5.2.jar</li><li>spring-ws-core-tiger-1.5.2.jar</li><li>spring-ws-support-1.5.2.jar</li><li>spring-xml-1.5.2.jar</li><li>stax-api-1.0.1.jar</li><li>XmlSchema-1.3.2.jar</li></ul></li><li>zgodnie z FAQ <span style="font-style: italic;">Spring WebServices</span> należy następnie "przykryć" wybrane <span style="font-style: italic;">jary</span> z <span style="font-style: italic;">Websphere</span> . Mamy następujące możliwości: <ul><li>ustawić sposób ładowania klas na <span style="font-style: italic;">Parent-Last</span> dla modułu webServices,</li><li>ustawić sposób ładowania klas na <span style="font-style: italic;">Parent-Last</span> dla całej aplikacji,<br /></li><li> zdefiniować własny <span style="font-style: italic;">classloader</span>, który będzie nadrzędnym <span style="font-style: italic;">classloaderem</span> dla <span style="font-style: italic;">classloadera </span>aplikacji,</li></ul>Ze względu na to, że kilka razy miałem problemy ze względu na "maintaince" aplikacji, dla której zostały zmienione domyślne sposoby ładowania klas ("przypadkowe" ładowanie klas z <span style="font-style: italic;">servlet-api.jar</span> z katalogu <span style="font-style: italic;">WEB-INF/lib</span>), zdecydowałem się na zdefiniowanie własnego <span style="font-style: italic;">classloadera</span>. <span style="font-style: italic;"><br /></span></li><li>Definiowanie własnego <span style="font-style: italic;">classloadera </span>jest szczegółowo opisane w dokumentacji <span style="font-style: italic;">Websphere</span> i polega na wykonaniu następującyh kroków<span style="font-style: italic;">:</span><ul><li>zdefiniowaniu <span style="font-style: italic;">shared-library</span>, w której znajdować się będą jary zawierające klasy, które to mają być ładowane przez tworzony <span style="font-style: italic;">classloader. </span>Nasza nowo tworzona <span style="font-style: italic;">shared-library</span> musi zawierać następujące jary(wszystkie skopiowane z katalogu <span style="font-style: italic;">lib </span>dystrybucji <span style="font-style: italic;">Spring WebServices</span>):<ul><li>wsdl4j-1.6.1.jar</li><li><span style="font-family:monospace;"></span>xalan-2.7.0.jar<span style="font-family:monospace;"></span></li><li><span style="font-family:monospace;"></span>xercesImpl-2.8.1.jar</li></ul></li><li>zdefiniowaniu nowego <span style="font-style: italic;">classloadera </span>na poziomie serwera i przypięciu do niego utworzonej w poprzednim punkcie <span style="font-style: italic;">shared-library</span>. Ważne jest, aby sposób ładowania klas dla tego <span style="font-style: italic;">classloader </span>był ustawiony na <span style="font-style: italic;">Parent-Last</span> (czyli <span style="font-style: italic;">Application-First</span>).<br /></li></ul></li><li>W końcu w naszej aplikacji musimy zdefiniować <span style="font-style: italic;">bean</span>, który będzie definiował specyficzną dla środowiska uruchomieniowego fabrykę do tworzenia obiektów SOAPMEssage:<pre class="brush: xml"><bean id="messageFactory" class="org.springframework.ws.soap.saaj.SaajSoapMessageFactory"><br /> <constructor-arg> <br /> <bean class="com.ibm.ws.webservices.engine.soap.MessageFactoryImpl"></bean><br /> </constructor-arg><br /></bean></pre><br /></li></ul>W takiej konfiguracji korzystam z dobrodziejstw <span style="font-style: italic;">Spring WebSevices. </span>Ze względu na to, że <span style="font-style: italic;">WAS 6.1.0.x</span> implementuje tylko <span style="font-style: italic;">SAAJ 1.2, </span>moje<span style="font-style: italic;"> endpointy </span>obsługują komunikaty<span style="font-style: italic;"> SOAP </span> w wersji<span style="font-style: italic;"> 1.1</span>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-91854408943239986762009-03-10T04:42:00.000-07:002009-03-11T11:48:34.967-07:00Spring 3.0 Talk with Arjen PoutsmaWczoraj wybrałem się do Warszawy na prezentacje Arjena Poutsmy na temat Spring 3.0.<br />Prezentacje odbyła się przy współpracy SpringSource i Warszawa JUG. Oficjalnie odbywały się zapisy przez Internet na prezentacje, była lista uczestników i lista rezerwowa, do tych pierwszych przychodził mail z biletem, który trzeba było wydrukować, aby wejść do środka.<br />Wszystko okazało się niezłą ściemą, ale i tak ludków przybyło całkiem sporo.<br />Prezentacja odbywała się w tej samej sali jak podczas WarsJava, ale publika tym razem o wiele bardziej dopisała, i dla kilku osób zabrakło krzeseł.<br /><br />Zgodnie z zapowiedziami Arjen Poutsma miał w godzinach 17-20 zaprezentować nam głównie co nowego jest przygotowywane w Spring 3.0. Po przeczytaniu abstractu prezentacji zapowiadało się naprawdę ciekawie:<br /><ul><li><span style=";font-family:arial,helvetica,sans-serif;font-size:85%;" >New features in Spring 3.0<br /></span></li><li><span style=";font-family:arial,helvetica,sans-serif;font-size:85%;" >Performance tuning Spring 3.0<br /></span></li><li><span style=";font-family:arial,helvetica,sans-serif;font-size:85%;" >An overview of Spring Projects<br /></span></li><li><span style=";font-family:arial,helvetica,sans-serif;font-size:85%;" >Enterprise capabilities for Spring</span></li></ul>Zaczęliśmy z małym opóźnieniem, ale przynajmniej pizza przyjechała na czas :) i podczas gdy publikuje zajadała się pierwszą z 2 zaplanowanych dostaw, speakera jeszcze nie było.<br />Pojawił się z około 15 minutowy opóźnieniem, po rozłożeniu maca i posileniu się pizzą mogliśmy zaczynać.<br />Już na początku, okazało się, że Arjen Poutsma ma przygotowane 2 prezentację i zacznie od tej bardziej ogólnej: p.t „7 reasons to use Spring”. Zaczęło się od nieśmiertelnej maksymy autorstwa Alana Kaya „Simple things should be simple, complex things should be possible” i później kolejno zostały omawiane poszczególne powody, dla których warto wybrać Springa:<br /><ol><li>DI - Nastąpiła krótka opowieść o POJO, jako o obiektach javowych niezależnych od środowiska, - czyli takich które nie posiadają żadnych „environment specific imports”i zależności od żadnych <span style="font-style: italic;">lookup methods</span>. Dalej krótka opowieść czemu POJO są takie fajne i czemu <span style="font-style: italic;">EJB</span> w wersji 2 nie było fajne. Potem był mały przykładzik, w którym to można było zobaczy w akcji mechanizm DI operujący na POJO, gdy konfiguracja została opisana raz to w XML, raz za pomocą annotacji. Mnie bardziej zaciekawił slajd p.t „ Why not use Java EE DI ?”, w którym to zostało podkreślone, że w JAVA EE „wstrzykiwanie zależności” polega na „wstrzykiwaniu” obiektów z <span style="font-style: italic;">JNDI</span>, czyli takie „wstrzykiwanie” nie będzie działać w środowisku <span style="font-style: italic;">JUNIT</span>. </li><li>JdbcTemplate –zaczęło się od krótkiego przypomnienia, że do pewnych zadań (batch operations, stored procedures, sophisticated/analytic queries) lepiej jest cały czas korzystać z czystego <span style="font-style: italic;">JDBC</span> niż całej maści różnych ORM. Następnie typowy przykład ile się trzeba namęczyć, jeśli chodzi o ilośćniezbędnych linii kodu oraz o odpowiednie zamykanie zasobów, aby korzystać z <span style="font-style: italic;">JDBC API</span>. Używanie <span style="font-style: italic;">JdbcTemplate</span> w takich sytuacjach nie tylko pozwoli nam drastycznie zmniejszyć ilość kodu, która musielibyśmy napisać, ale też zajmie się zamykaniem zasobów: <span style="font-style: italic;">PreparedStatement, ResultSet, Connection</span>. Ze względu na to, że każda operacja wyciągnięcia danych za pomocą SQL sprowadza się jedynie do podania zapytania, jego parametrów oraz ewentualnego „obrobienia” <span style="font-style: italic;">ResultSeta</span> reszta to „boiler plate code”, którym lepiej się nie zajmować i zostawić go <span style="font-style: italic;">JdbcTemplate</span>.</li><li>Exception hierarchy – równie powiązane z dostępem do BD, stworzona w Springu hierarchia wyjątków, która umożliwia w spójny sposób radzenie sobie z wszelkiego rodzajami błędami związanymi z dostępem do bazy danych. Kod błędu ze sterownika <span style="font-style: italic;">JDBC </span>jest mapowany w zależności od używanej bazy danych na wyjątek określonego typu, który możemy złapać i obsłużyć jeśli będziemy mieć taką potrzebę, np. złapać <span style="font-style: italic;">DeadlockLoserDataAccessException</span> aby ponowić operację. </li><li>AOP – nadmienione zostały podstawowe pojęcia: <span style="font-style: italic;">pointcut, jointpoint, aspect, cross cutting concern,</span> wraz z typowymi przykładami wykorzystania <span style="font-style: italic;">AOP</span>: transakcji, logowanie, bezpieczeństwo, cachowanie. <span style="font-style: italic;">AOP</span> przede wszystkim ma służyć aby poradzić sobie z 2 problemami:<ul><li>Code tangling – gdy klasa/metoda nie zajmuję się wyłącznie logiką dla której została stworzona, ale dodatkowo zawierają kod związany z innymi funkcjonalnościami: sprawdzanie warunków bezpieczeństwa, obsługa logowania/transakcji.</li><li>Code scattering – w przypadku gdy kod (dotyczy to choćby pojedynczego wywołania) związany z daną sprawą(aspektem) jest rozsiany pomiędzy wiele klas, które w swoich założeniach powinny zajmować się jedynie swoją funkcjonalnością. </li></ul>Potem nastąpił sztandarowy przykładzik logowania przy wykorzystaniu Spring AOP plus krótkie wyjaśnienie czym Spring AOP różni się od AspectJ.<br /></li><li>Transactional - czyli możliwość deklaratywnego definiowania transakcji.<br /></li><li>Scripting Languages - możliwość definiowanie <span style="font-style: italic;">beanów</span> w językach skryptowych uruchamianych na JVM: Groovy, BeanShell, JRuby. Ma to umożliwiać tworzenie rozwiązań w stylu "mix 'n match", w których to <span style="font-style: italic;">backend </span>systemu będzie napisany w Javie a <span style="font-style: italic;">fronetend </span>w języku skryptowym, a wszystko będzie spięte przez Springa.</li><li>OSGI - jako technologia, która pozwala poradzic sobie z 2 problemami w świecie Javy: "JAR hell" oraz "<span style="font-style: italic;">module encapsulation</span>". Dodatkowo wymienione zostały 2 produkty SpringSource, związane z OSGI: <span style="font-style: italic;">Spring Dynamic Modules</span> oraz <span style="font-style: italic;">Spring DM Server</span>. Arjen pokazał w jaki łatwy sposób w Springu opakować istniejącego <span style="font-style: italic;">beana</span> aby stał się usługą OSGI, bez potrzeby uzależniania naszego kodu od inwazyjnego API OSGI</li></ol>Na tym skończyła się pierwsza prezentacja, nastąpiła 15 minutowa przerwa, po której Arejn rozpoczął omawianie "RESTFul Web Application in Spring 3.0".<br />Nie mogło się obejść od krótkiego omówienia czym jest REST i co w nim chodzi. Były to bardziej pobieżne informację do tych, które umieściłem <a href="http://jmilkiewicz.blogspot.com/2009/01/rest-in-peace.html">tutaj</a>. Dodatkowo Arjen uczulił szczególnie na nadmierne/niewłaściwe wykorzystywanie parametrów zapytań, które w swoim założeniu powinny być "parametrami wejściowymi algorytmów", np. http://www.google.pl/search?q=java<br />Często zostają jednak użyte do innych celów (przemycanie faktycznej metody do wykonania), poza tym mogą być ignorowane przez proxy. 2 sprawą, która powinna wzbudzić naszą czujność są czasowniki w URL, ponieważ URL dotyczy zasobu/bytu który jest "rzeczą" a nie czynnością.<br />Pozostała cześć prezentacji okazała się kopią tego co znajduję się na <a href="http://blog.springsource.com/2009/03/08/rest-in-spring-3-mvc/">blogu Arjena.</a><br /><br />Podsumowując, prezentacja była całkiem niezła,(zresztą jak wszystkie te przeprowadzane przez konsultantów SpringSource), ale skierowana dla osób, które nie znają Springa lub znają go bardzo pobieżnie. Było zbyt mało informacji na temat Spring 3.0 (który tak na marginesie oprócz REST, nowego EL oraz odcięcia się od Javy 1.4 nie wnosi zbyt wiele nowego), a zgodnie z abstractem prezentacji to właśnie miało być tematem przewodnim . Dodatkowo w wielu miejscach argumenty przedstawiane przez Arjena, które miały świadczyć o wyższości Springa nad innymi technologiami były trochę "out-of-date"<span><span>.<br /></span></span>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-16450997247917740842009-02-20T07:17:00.000-08:002009-07-21T10:13:02.006-07:00Mix SpringTestContext Framework i EasyMockW ramach aktualnego projektu zastanawiałem się nad zagadnieniem jego testowania. Staram się aby projekt był prowadzony zgodnie z TDD i do tego celu korzystamy intensywnie z <a href="http://www.easymock.org/">EasyMock</a> oraz <a href="http://static.springframework.org/spring/docs/2.5.x/reference/testing.html#testcontext-framework">Spring TestContext Framework</a>. Wszystko działa jak należy, ale pojawił się problem z testowaniem <strong>integracyjnym</strong> (przy wykorzystaniu Spring TestContext Framework), gdy ścieżka wykonania testu obejmuję komponenty, które to są zależne od innych (dostarczonych z zewnątrz) elementów.<br /><br />Zgodnie z <em>best-practices</em>, w ramach projektu <em>context</em> Springowy zostaly podzielony na oddzielne pliki nie tylko ze względu na warstwę systemu(web,service,DAO, adapter), ale także ze względu na infrastrukturę (Junit, Tomcat, Websphere).<br /><br />Początkowo wydawało się, że wystarczy stworzyć dodatkowy plik <em>contextu </em>- tylko na potrzeby testów- który mógłby defniować <em>beany</em> odpowiadające systemom zewnętrznym. W takim przypadku należy stworzyć klasy tych <em>beanów</em>, które pełniłyby rolę <em>stubów.</em> Podejście takie ma 1 zasadniczą wadę: w przypadku gdy nasza logika związana z interakcją z systemem zewnętrznym jest bardziej skomplikowana niż "fire 'n forget"to możliwość definiowania zachowania stuba jest ograniczona. Logika działania stuba jest zdefiniowana "a priori" w jego klasie i ewentualna zmiana zachowania wiąże się z utworzeniem nowej klasy/rozbudową istniejącej. Osobiście nie jestem zwolennikiem tworzenia dużej ilości dodatkowego kodu wyłącznie na potrzeby testów, tzn. czasami nie da się od tego uciec, ale jeżeli istnieje alternatywne rozwiązanie...<br />Trywialny przykład interfejsu do systemu zewnętrznego:<br /><pre class="brush: java">public interface UserAuthenticationManager {<br /><br /> int logUser(String name, String pass);<br /><br />}<br /></pre><br />Wartość funkcji <em>logUser</em> jest ściśle określona, ale dla uproszczenia przyjmijmy, ze interesują nas 4 przypadki:<br />nie ma usera o danej nazwie, name/pass nie pasują, user zablokowany, wymagana zmiana hasła usera. Każda z takich sytuacji jest mapowana na inną wartość liczbową i na tej podstawie następuję specyficzne przetwarzanie.<br />Stosując podejście przedstawione powyżej, możemy albo utworzyć 4 klasy <em>stubów</em>, umieścić je w osobnych plikach <em>contextu</em> i odpowienio do scenariusza ładować jeden z nich, albo utworzyć 1 klasę <em>stuba</em> a w niej umieścić odpowiednie "if" aby pokryć wszystkie 4 przypadki.<br />Oba podejścia są słabe: albo rozdrabniamy konfiguracje, co powoduję niesamowity przyrost plików konfiguracyjnych, albo zajmiemy się pisaniem logiki, która na podstawie odpowiednich wartości (wartości parametrów metody, albo wartości umieszczonej w zmiennej <em>ThreadLocal</em>) zwróci nam wartość odpowiednią do scenariusza jaki w tym momencie testujemy.<br /><br />Idealne w sytuacji wydaje się połączenie Spring TestContext Framework oraz EasyMock. Scenariusze testów integracyjnych byłyby sterowane przez Spring, a w przypadku odwoływania się do systemów zewnętrznych do akcji wchodziłyby <em>mocki, dla których </em>zachowanie zostałoby określone(nagrane) bezpośrednio w metodzie testowej. Z technicznego punktu widzenia chodzi o to, aby móc podmieniać w czasie wykonywania testu wybrane <em>beany</em> Springowe (w moim przypadku <em>singletony</em>), na utworzone <em>mocki.</em><br />Pierwszą przeszkodą było agresywne inicjowanie <em>beanów</em> Springowych, które są <em>singletonami</em>. Problem polegał na tym, ze zanim metody testowe zostaną odpalone (a w nich miałaby się odbyć podmiana) <em>context</em> Springowy zostanie zaczytany, a <em>beany</em> będące singletonami utworzone. Pewnym obejściem, może być ustawienie atrybut <em>default-lazy-init </em>na<em> true</em> w elemencie <beans> w plikach <em>contextu. </em><br />Pomimo, że rozwiązuje to problem, jest to sprzeczne z filozofią związaną z agresywnym tworzeniem <em>beanów</em>, dzięki której pomimo dłuższego czasu startu aplikacji błędy związane z tworzeniem się contextu bardzo szybko "wylatują". Jest to szczególnie ważne przy uruchamianiu aplikacji w środowiskach integracyjnym i produkcyjnym. Dlatego zdecydowałem się znaleźc alternatywne rozwiązanie, dzięki któremu nie musiałbym "naginać"konfiguracji Springowej do testowania.<br />Szczęśliwie twórcy Spring TestContext Framework umozliwiają bardzo prosto określenie klasy, która to będzie odpowiedzialna za odczytywanie i ładowanie <em>ApplicationContext</em> a sprowadza się to do podania odpowiedniej klasy w annotacji <em>ContextConfiguration</em><br /><pre class="brush: java">@RunWith(SpringJUnit4ClassRunner.class)<br />@ContextConfiguration(locations={"/context.xml"}, loader=LazyContextLoader.class)<br />public class Test{<br />...<br />}<br /></pre><br />Klasa <em>LazyContextLoader</em> jest odpowiedzialna za wczytywanie kontekstu i jej implementacja jest niemal identyczna do klasy <em>GenericXmlContextLoader:</em><br /><pre class="brush: java">public class LazyContextLoader extends AbstractGenericContextLoader {<br /><br /> @Override<br /> protected BeanDefinitionReader createBeanDefinitionReader( final GenericApplicationContext context ) {<br /> XmlBeanDefinitionReader result = new XmlBeanDefinitionReader( context );<br /> result.setDocumentReaderClass( LazyInitByDefaultBeanDefinitionDocumentReader.class );<br /> return result;<br /> }<br /><br /> @Override<br /> public String getResourceSuffix() {<br /> return "-context.xml";<br /> }<br />}<br /></pre><br />Zmieniona została tylko implementacja interfejsu <em>BeanDefinitionDocumentReader, </em>który to odczytuję pliki XML z defincjami <em>beanów</em>.<br />Zadaniem <em>LazyInitByDefaultBeanDefinitionDocumentReader</em> jest ustawienie w locie na <em>root</em> każdego z zaczytanych plików XML atrybutu <em>default-lazy-init</em> na wartość <em>true</em><br /><pre class="brush: java">public class LazyInitByDefaultBeanDefinitionDocumentReader extends DefaultBeanDefinitionDocumentReader {<br /><br /> @Override<br /> protected BeanDefinitionParserDelegate createHelper( XmlReaderContext readerContext, Element root ) {<br /> root.setAttribute( BeanDefinitionParserDelegate.DEFAULT_LAZY_INIT_ATTRIBUTE,"true" );<br /> return super.createHelper( readerContext,root );<br /> }<br />}<br /></pre><br />W ten sposób udało się "czysto" zaczytać konfiguracje Springowa bez inicjalizacji <em>beanów</em>.<br />Następnym krokiem pozostała podmiana istniejących beanów, która okazała się dość prosta:<br /><pre class="brush: java"><br />@RunWith(SpringJUnit4ClassRunner.class)<br />@ContextConfiguration(locations={"/context.xml"}, loader=LazyContextLoader.class)<br />public class Test{<br /><br /> @Autowired<br /> private GenericApplicationContext context;<br /><br /> @Test<br /> @DirtiesContext<br /> public void testMethod(){ <br /> context.removeBeanDefinition( "userAuthenticationManager" ); <br /> UserAuthenticationManager mock = EasyMock.createMockUserAuthenticationManager.class );<br /> EasyMock.expect( mock.logUser( (String)EasyMock.notNull(),(String)EasyMock.notNull() ) ).andReturn( 12 );<br /> EasyMock.replay( mock );<br /> context.getBeanFactory().registerSingleton( "userAuthenticationManager", mock );<br /> ...<br />}<br /></pre><br />Należy pamiętać jednak o kilku sprawach:<br /><ul><li><em>beany</em>, które chcemy podmienić nie mogą być bezpośrednio <em>injectowane</em> w klasie testów, ani też nie moga być składowymi innych beanów, które mają byc <em>injectowane</em> (poprzez wkorzystywanie <em>@Autowired</em>)</li><li>metody testowe, które zmieniają <em>context</em> powinny byc oznaczone annotacją <em>@DirtiesContext</em></li><li>Przedstawiony powyżej kod, wrzuca <em>bean</em> do <em>contextu</em> jako w pełni zainicjalizowany komponent, tzn. nie są na nim odpalane jakiekolwiek metody związane z cyklem życia <em>beana,</em> nie podlega konfiguracji AOP zawartej w plikach <em>contextu.</em></li><li>Dla mojego projektu wystarczyło podmieniać<em> singletony</em>, nie potrzebowałem podmieniać <em>beanów</em> o innym cyklu życia</li><li>"leniwa" inicjalizacja <em>beanów</em> pozwala zaczytać niepełny <em>context</em> Springowy, czyli przykładowo taki, który nie musi zawierać definicji wszystkich beanów. Dopóki nie odwołamy się, za pomocą <em>context.getBean() </em>lub <em>@Autowired, </em>do <em>beana</em>, który nie został zdefiniowany, lub nie zostały zdefiniowane dla niego wszystkie zależne <em>beany,</em> możemy dowolnie "mieszać" w konfiguracji</li><li>Spring cachuje zaczytany <em>context/contexty </em>aplikacji na podstawie wartości <em>locations</em><br />annotacji <em>@ContextConfiguration.</em> Ze względów wydajnościowych sensownie jest wydzielić testy integracyjne, które w swoim działaniu mogą wywoływać systemy zewnętrzne od pozostałych testów integracyjnych systemu. Wydzielenie te będzie jedynie poprzez określenie dodatkowego (sztucznego) pliku <em>contextu aby </em>pozostałe testy integracyjne (te, które nie dotykają systemów zewnętrznych) mogły swobodnie korzystać z cachowanego <em>contextu</em>.</li></ul>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-77426927755438303402009-01-24T06:37:00.000-08:002009-02-01T09:38:01.864-08:00Scaling Hibernate: tips, recipes and new perspectives oraz Tune this!<p>Po godzinnej przerwie rozpoczęły się sesję BOF. Jak się później okazało wszystkie sesje BOF prowadzone były w 2 wąskich, dusznych salkach ze słabym sprzętem audio-video. </p><br /><p><br /><a href="http://www.devoxx.com/display/JV08/Scaling+Hibernate+-+tips%2C+recipes+and+new+perspectives">Scaling Hibernate: tips, recipes and new perspectives</a> - zgodnie z zapowiedziami Emmanuel Bernard miał przedstawić sposoby optymalizacji aplikacji opartych o Hibernate. Po minutowym wstępie, w którym omówił "klasyczne" metody optymalizacji: <em>batch size</em> czy <em>fetch size</em> ciut więcej można się było dowiedzieć o <em>2nd level cache</em>: </p><ul><li>nie ma sensu używać <em>2nd level cache</em> dla bardzo dużych woluminów danych: jak mamy milion userów w systemie to nie ma sensu cachowac takiej ilości danych: skończy się to Out of memory exception lub sytuacją w której tylko część danych tak naprawdę będzie w cache i to nie koniecznie te, które byśmy chcieli</li><li>ma być już możliwe używanie <em>JBoss Cache</em> jako transakcyjnego <em>2nd level cache</em> - miało zostać to ostatnio naprawione </li></ul><p>Pojawiła się wzmianka o użyciu <em>Hibernate</em> w systemach masowego przetwarzania/wsadowych i korzystaniu z takich dobrodziejstw jak: <em>batch insert, batch update, ScrollableResults, StatelessSession</em> (pobierane encje są automatycznie odłączane i mogą zostac usunięte przez GC). Ja osobiście dodałbym do tego bezpośrednią możliwość wykonania operacji update/delete z poziomu <em>Hibernate</em> za pomocą <em>session.createQuery("...").executeUpdate();</em><br />Następnie przedstawiono różne możliwości radzenia sobie w bardziej specyficznych przypadkach, np. bardzo duża ilość danych, bardzo duże obciązenie baz danych, dane z różnych (politycznych) względów muszą być rozdzielone.<br />Jeśli chodzi o seperacji danych to wyróżniamy następujące opcje:</p><ul><li>najprostszą możliwością (choć nie do końca można to nazwać separacją) jest separacja logiczna: wszystkie dane są przechowywanych w tych samych tabelach a separacja odbywa się na poziomie aplikacyjnym(logicznym) za pomocą <em>Hibernate Filter</em> lub jakiś innych tricków. Od razu przypomniał mi się mechanizm przedstawiony przez Alefa Arendsena podczas Spring ONE. Chodziło o to, aby za pomocą Spring AOP wrzucić do sztucznej tabeli identyfikator aktywnej transakcji oraz identyfikator klienta. Encje nie zostały "zbindowane" do tabel a do widoków, których zadaniem jest filtrowanie danych zawartych w tabeli po identyfikatorze klienta, którego aktualna wartość (wartość do "zmatchowania") znajduje się w sztucznej tabeli</li><li>można rozproszyć dane na wiele schematów i są tutaj 2 podejścia: 1 <em>SessionFactory</em> na 1 schemat (problem skalowalności) lub 1 <em>SessionFactory</em> dla wszystkich schematów (super login, wymaga przepisania zapytań - uwzględnienie nazwy schematów lub implementacji metody <em>onPrepareStatement</em> interfejsu <em>org.hibernate.Interceptor</em> - doklejenie do zapytania nazwy schematu). Ma tu się pojawiać dodatkowo problem z <em>2nd level cache</em>, ale nie jestem pewien czy dobrze rozumiem dlaczego</li><li>wykorzystanie mechanizmów bazy danych, szczególnie kuszącym rozwiązaniem jest <em>Oracle VPD </em>(filtr na poziomie bazy danych)</li></ul><p>Jeśli dodatkowo danych jest tak dużo i zdecydujemy się je rozpraszać pomiędzy wiele baz danych to Emmanuel Bernard zaprezentował 2 najpowszechniejsze rozwiązania tego problem, oba dotyczące warstwy apikacyjnej:</p><ul><li><em>Homogenous nodes</em> - każda instancja serwera aplikacyjnego ma pulę połączeń do każdej z bazy danych. Podejście takie ma mie wiele problemów natury wydajnościowej: duża ilość połączeń do bazy danych, zużycie pamięci, wolny start aplikacji (na każdej instancji startuje tyle <em>SessionFactory</em> ile mamy baz danych)</li><li>Specialized nodes - poszczególne instancje serwera aplikacyjnego mają dostęp jedynie do określonych baz danych. Ważny jest tutaj odpowiedni mechanizm <em>load-balancing</em>, który musi rozrzucać żądania użytkowników na podstawie żądanej funkcjonalności. Zaletą takiego podejścia jest skalowalność oraz wykorzystanie zasobów, ale mogą pojawić się problemy gdy nie istnieje żadna instancja serwera posiadająca dostęp do wszystkich danych, których będzie wymagać żądanie użytkownika</li></ul><p>Rozwiązaniem ma być <em>Hibernate Shards</em>, który jest mechanizmem partycjonowanie poziomego (<em>horizonal partitioning</em>), czyli każda z baz danych posiada ten sam model, a jedynie różne dane. Dla porównania, partycjonowanie pionowe (<em>vertical partitioning</em>) polega na podziale danych ze względu na funkcjonalność i każda baza danych ma inny model : na 1 trzymamy dane użytkowników, na 2 tabele produktów itd. Na poziomie architektury <em>Hibernate Shards</em> rozszerza bibliotekę Hibernate i stanowi warstwę, która enkapsuluje logikę wynikającą z rozproszenia danych pomiędzy wiele baz danych, a z technicznego punktu widzenia opakowuje wiele instancji <em>SessionFactory</em> (podłączonych fizycznie do różnych baz o tym samym modelu) dostarczając użytkownik ujednolicony sposób dostępu do danych. Twórcy biblioteki dokonują wielkich starań i zabiegów, aby do jej obsługi móc korzystać ze znanego i sprawdzonego <em>Hibernate </em>API, istnieją odpowiednie implementacje takich interfejsów jak: <em>SessionFactory, Session, Query, Criteria</em>. Dodatkowo dochodzą do tego bardziej złożone sprawy wynikające ze specyfiki rozproszenia danych, które to zostały wyrażone za pomocą interfejsów:</p><ul><li>ShardSelectionStrategy - decyduje o tym, na której partycji utworzyć obiekt</li><li>ShardResolutionStrategy - decyduje o tym na jakiej partycji szukać obiektu o zadanym kluczu głównym</li><li>ShardAccessStrategy - w jaki sposób wykonywać zapytania, które dotyczą więcej niz 1 partycji</li></ul><p>Aktualnie <em>Hibernate Shards</em> udostępnia implementacje (lub kilka implementacji) każdego z tych interfejsów, ale oczywiście bardzo łatwo stworzyć własną.<br />Jeśli chodzi o zapytania to jak na razie <em>Hibernate</em> w ograniczonym zakresie wspiera <em>Criteria Query</em> (np. avg po poszczególnych partycjach) oraz nie wspiera <em>HQL</em> (problem z parserem). Dodatkowo dany graf obiektów nie może obejmować więcej niż pojedynczej partycji, co osobiście uważam za wielkie ograniczenie. Wyobraźmy sobie przypadek gdy tworzymy <em>rich data model</em> (nasze encje posiadają powiązania do innych encji a nie jedynie ich identyfikatory) i mamy powiązania do generycznych danych, np. kraje (których nie ma sensu rozpraszać). W takim przypadku jak na razie polecane jest mapowanie "nieobiektowe" takiej relacji, tabele krajów przechowywać na jednej z partycji a do szybszego dostępu do niej trzymać ją w cache w pamięci. W przyszłości planuje się zrobić pewnego rodzaju mechanizm replikacji, który to umożliwiałby spójne przechowywanie takiego typu danych w każdej z partycji a dzięki temu możliwe będzie skorzystanie z dobrodziejstw baz danych (ograniczenie foreign key) oraz <em>Hibernate</em> (rich data model).<br />Przewiduję się dodatkowo wiele problemów z organizacją/reorganizacja danych, aby przygotować je na rozproszenie. Dotyczy to nie tylko inicjalnego rozproszenia, ale co gorsza podziału istniejących juz partycji. Do tego celu stworzony został mechanizm <em>virtual shards</em>, który jest warstwą pośrednią pomiędzy <em>Hibernate Shards</em> a instancjami baz danych czyli partycjami fizycznymi. Podział na partycje wirtualne powinien być dokonany ze względu na wymagania biznesowe i brać pod uwagę "przyszłe potrzeby" systemu. Wiele partycji wirtualnych może początkowo zostać zbindowanych do pojedynczej partycji fizycznej. W razie potrzeby, w dalszej fazie istnienia systemu partycja fizyczna może zostać "przepięta" do dedykowanej dla siebie partycji fizycznej.<br />Wniosek z tej części prezentacji jest taki, aby próbować wszystkiego, by uniknąć rozpraszania swoich danych. Jednak gdy osiągniemy punkt, w którym nic więcej nie da się zrobić i pozostaje nam jedynie fizyczne rozproszenie danych, a dodatkowo używamy <em>Hibernate</em> to projekt <em>Hibernate Shards</em> na pewno jest w stanie nam pomóc. Następnie Emmanuel Bernard przeszedł do "swojego" tematu, czyli <em>Hibernate Search</em> i właśnie wydawanej ksiązki "Hibernate Search in Action", której jest współautorem. </p><p><a href="http://www.devoxx.com/display/JV08/Tune+This">Tune This!</a> - to było już 2 spotkanie tego dnia z Kirkiem Pepperdine. Po minimalnym wstępie omawiającym miejsca, które mogą mieć negatywny wpływ na wydajność: aplikacja, JVM/OS, hardware oraz paru ogólnikowych wskazówkach związanych z wydajnością, przeszliśmy do konkretnych przykładów. Kirkiem Pepperdine przygotował aplikację, a widownia na podstawie różnych miar zużycia zasobów i wyników z profilera miała ocenić co jest przyczyną słabej wydajności aplikacji. W pierwszym przypadku mieliśmy do czynienia z bardzo dużym obciążeniem procesora. Proces javy nie był głównym konsumentem CPU, a najbardziej obciążał sam system operacyjny. Profiler wskazywał, ze większość wątków było w stanie "wait". Wszystko to oznaczało, że nasza aplikacja ma problemy z <em>lockami.</em> Dopiero po wysunięciu takiej propozycji Kirkiem Pepperdine pokazał nam kod źródłowy aplikacji i faktycznie, ze względu na niewłaściwe użycie <em>synchronized</em> aplikacja działała tak wolno. Po usunięciu problemu, czas działania aplikacji był juz dużo lepszy, jednak tym razem zużycie CPU przez proces java było bardzo wysokie. Powodem tego może być aplikacja sama w sobie lub też działanie GC. Z powodu braku czasu Kirk Pepperdine nie zaprezentował <em>hpjmeter</em>, które to uważa za wyśmienite narzędzie do analizy działania GC, a jedynie pokazał nam w jaki sposób za pomocą <em>HPROF </em>znaleźć, w którym miejscu kodu, aplikacja tworzy na stercie obiekty zajmujące największą ilość pamięci. Jego przykład był dość banalny i błąd polegał na bezsensownym tworzeniu obiektów typu <em>String.</em></p>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0tag:blogger.com,1999:blog-9185236033651750735.post-45834156754067390822009-01-22T04:33:00.000-08:002009-01-22T09:32:50.786-08:00Visual VM oraz Hibernate ToolsKolejną sekcją były prezentacje: Tools in Action<br /><br /><a href="http://www.devoxx.com/display/JV08/VisualVM+-+new+extensible+monitoring+platform">VisualVM - new extensible monitoring platform<br /></a>- tak na prawdę nie ma o czym pisać, po prostu zostało przedstawione narzędzie i jakiś tam mały przykład. Prezentacja bardzo podobna do tej autorstwa Adam Dudczaka <a href="http://www.jug.poznan.pl/wp-content/uploads/2008/10/jmx.pdf">http://www.jug.poznan.pl/wp-content/uploads/2008/10/jmx.pdf</a>. Nawet jeśli Kirk Pepperdine przedstawił jakieś tips'n tricks ja tego nie wyłapałem.<br /><br /><a href="http://www.devoxx.com/display/JV08/Making+full+use+of+Hibernate+Tools">Making full use of Hibernate Tools</a> - zaczęło się od przestrogi: "Don't overdo it". Max Rydahl Andersen pokazał działanie Hibernate Tools w JBoss Developer Studio. Podpowiadanie kodu w zapytaniach HQL i plikach .hbm, odpalanie zapytań bezpośrednio z IDE oraz duża ilość wizzardów do generacji różnych artefaktów wzbudziło zainteresowanie. Nie dało się ukryć, że nie wiele osób z widowni na bieżąco śledzi co się dzieje w Hibernate Tools.<br /><br />Omówiono bardziej szczegółowo mechanizm generowania artefaktów za pomocą Hibernate Tools, a cały schemat jest przedstawiony na rysunku poniżej.<br /><br /><p><br /></p><p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtqYeHPuYUC9cWI3eIgbU-Rc53JFq5WhcIjw1PRw4zRardpdcobAC3ZNzN3oGYC9SVLEgMRR-FhYmqWe79dh8yqI0GU_nnd4ZRkryIPmDpb312_uuTzbxwHPNFaEG6o1Ig1F_o7rsmgilJ/s1600-h/code_generation_1.png"><img id="BLOGGER_PHOTO_ID_5294128460267864290" style="DISPLAY: block; MARGIN: 0px auto 10px; WIDTH: 320px; CURSOR: hand; HEIGHT: 141px; TEXT-ALIGN: center" alt="" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtqYeHPuYUC9cWI3eIgbU-Rc53JFq5WhcIjw1PRw4zRardpdcobAC3ZNzN3oGYC9SVLEgMRR-FhYmqWe79dh8yqI0GU_nnd4ZRkryIPmDpb312_uuTzbxwHPNFaEG6o1Ig1F_o7rsmgilJ/s320/code_generation_1.png" border="0" /></a></p><p>Centralną część stanowi <em>meta model </em>reprezentowany przez klasę <em>org.hibernate.Configuration, </em>i który to może zostać zbudowany na podstawie różnych źródeł: pliki .hbm, klasy java z adnotacjami (Hibernate lub JPA), połączenie JDBC do baza danych . Następnie <em>meta model</em> jest "obrabiany" za pomocą odpowiedniego e<em>xportera, </em>który może generować określone artefakty, np. pliki .hbm, klasy JAVA z adnotacjami, schemat bazy danych oraz jego dokumentacje. Osobiście spodobała mi się wbudowana możliwość użycia biblioteki <a href="http://freemarker.sourceforge.net/">freemarker</a> jako <em>exportera - w</em>ystarczy jedynie stworzyć odpowiedni template. Dodatkowo Hibernate Tools daje możliwość dostępu do klas użytkownika z poziomu template, dzięki temu bardziej złożona logika generowania może zostać zawarta w javie a nie bezpośrednio w template.</p><p>Reszta prezentacji została poświęcona na różne aspekty związane z konfiguracją odczytywania meta modelu z bazy danych oraz mapowanie go do modelu Hibernate (<em>reverse engineering strategy</em>). Dostępna out-of-box strategia jest bardzo mocno konfigurowalna (nawet per tabela/kolumna), ale jeśli pojawią się specjalne wymagania to zawsze można stworzyć własną implementację. </p><p>Wszystko te mechanizmy są dostępne w JBoss Developer Studio lub JBoss Tools(plugin do eclipse) w postaci <em>wizzard</em>ów, ale takze z poziomu anta. Max Rydahl Andersen zapewniał, że <strong>wszystko</strong> to co można "wyklikać" w JBoss Tools jest jednocześnie dostępne jako taski ant. </p>milushttp://www.blogger.com/profile/04255791953013506623noreply@blogger.com0