Java Enums та перевизначення методів

Читаючи книгу Programming Groovy, я дізнався про можливість перевизначати методи в enums. Про це навіть не здогадувався раніше, і це мене справді здивувало.
І справді, нехай у нас є enum в якому вказані дні в тижні:

enum DAYS {
MON, TUE,
WED, THU,
FRI, SAT,
SUN;

public String is() {
return "working";
}
}

Що ж, MON.is().equals("working") має бути істинним; але подібне тяжко сказати, наприклад, для SUN.is().equals("working").
Звичайно тепер ми можемо визначити enum наступним чином:

enum DAYS {
MON, TUE,
WED, THU,
FRI,
SAT {
public String is() {
return "week-end, day 1";
}
},
SUN {
public String is() {
return "week-end, day 2";
}
};

public String is() {
return "working";
}
}

І все, тепер SUN.is().equals("week-end, day 2") == true.
Проблема вирішена, при чому, без збереження зайвого поля - вказівника на рядок.

Mac OS X

Yesterday I have been fully moved to the Mac OS X. Spent all day for finding and installing software. I little bit sad, because can't find java 1.6 and 32 bit version.

I was thinking about changing used OS from Windows to Mac OS X or to Linux (Ubuntu) a very long time already. But just now found free day to move.
Still, If I'll not find the solution with java update and sound bad work then I'll become Linux (Ubuntu) user.

Google App Engine & Java

Google нарешті таки вирішив, що наступна версія App Engine буде підтримувати Java. З самого початку пора. А ще би було добре позбутися декотрих дивних обмежень, як от, тривалість запиту, маскимальний розмір файлу тощо.

SoftReference & WeakReference: time of life

Well, I found some free time to write few articles. This one is about the SoftReference class from Java library.
SoftReference will help you to get the best variant between using memory and performance. Soft reference means that the value wrapped in soft reference will be garbage collected only and only if there are no free memory.
More about this. First I set -verbose:gc parameter for Java machine. If this parameter is set than GC logs will be output to the console. Also was set parameter -Xmx32m. The simple code:

void test() {
Reference<List> listRef = new SoftReference<List>(
Arrays.asList("1", "2", "3", "4", "5"));

List list = new LinkedList();
for (int i = 1; i <= 5; i++) {
list.add(new Object());

if (i == 5) {
if (listRef.get() == null) {
break;
}
i = 1;
}
}
}

Here the soft reference to the long string was created. The loop adds the references to the object to the linked list. Sometimes it checks whether the soft reference to the list of strings is not gc'ed; if it is than loop is breaked. Here is the output of GC:

[GC 896K->708K(5056K), 0.0059887 secs]
[GC 1604K->1603K(5056K), 0.0064902 secs]
[GC 2499K->2498K(5056K), 0.0064698 secs]
[GC 3394K->3393K(5056K), 0.0062198 secs]
[GC 4289K->4287K(5184K), 0.0061248 secs]
[Full GC 4287K->4287K(5184K), 0.0378679 secs]
[GC 5120K->5119K(8000K), 0.0141557 secs]
[GC 6015K->6014K(8000K), 0.0069241 secs]
[GC 6910K->6908K(8000K), 0.0064464 secs]
[GC 7804K->7804K(8768K), 0.0061396 secs]
[Full GC 7804K->7804K(8768K), 0.0595140 secs]

We may see that more than one Full GC were run before the soft reference was cleaned up.
The same test for the WeakReference (change new SoftReference(...) into new WeakReference(...)) prints next output:

[GC 896K->707K(5056K), 0.0063061 secs]

So, weak reference is cleaned up after the next first GC runs.
These tests were made on Java 1.6.0_10.
The outputs for Java 1.5.0_07 are respectively

[GC 512K->387K(1984K), 0.0047419 secs]
[GC 899K->898K(1984K), 0.0046456 secs]
[GC 1410K->1409K(1984K), 0.0048894 secs]
[GC 1921K->1920K(2496K), 0.0046743 secs]
[Full GC 1920K->1920K(2496K), 0.0203305 secs]
[GC 2431K->2430K(3776K), 0.0103943 secs]
[GC 2942K->2941K(3776K), 0.0048556 secs]
[GC 3453K->3452K(4032K), 0.0047855 secs]
[Full GC 3452K->3452K(4032K), 0.0340532 secs]
[GC 3964K->3963K(6332K), 0.0044411 secs]
[GC 4475K->4474K(6332K), 0.0049126 secs]
[GC 4986K->4985K(6332K), 0.0046232 secs]
[GC 5497K->5496K(6332K), 0.0045682 secs]
[GC 6008K->6007K(6588K), 0.0047892 secs]
[Full GC 6007K->6007K(6588K), 0.0523812 secs]
[GC 6775K->6774K(10848K), 0.0061404 secs]
[GC 7542K->7540K(10848K), 0.0065743 secs]
[GC 8308K->8307K(10848K), 0.0067296 secs]
[GC 9075K->9074K(10848K), 0.0066386 secs]
[GC 9842K->9841K(10848K), 0.0066439 secs]
[GC 10609K->10608K(11488K), 0.0066955 secs]
[Full GC 10608K->10606K(11488K), 0.1112694 secs]

and

[GC 512K->387K(1984K), 0.0049869 secs]


Since, these results show that Java 6 works with memory better than Java 5 and cleans up the soft reference faster than it is in Java 5.

Spring MVC + FreeMarker + Tiles 2

Може комусь і знадобиться ще. Тут описується як прикрутити Spring MVC та FreeMarker та Tiles 2 так, щоб вони жили та працювали разом.
Задача: Tiles2 повинен організовувати шаблони створені на основі FreeMarker (хоча можна і не тільки FreeMarker, можна поєднувати). Для того, щоб досягнути це необхідно виконати декотру конфігурацію. У файлі web.xml

<servlet>
<servlet-name>pages</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>pages</servlet-name>
<url-pattern>*.page</url-pattern>
</servlet-mapping>

<servlet>
<servlet-name>freemarker</servlet-name>
<servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>

<init-param>
<param-name>TemplatePath</param-name>
<param-value>/</param-value>
</init-param>
<init-param>
<param-name>NoCache</param-name>
<param-value>true</param-value>
</init-param>
<init-paramv
<param-name>ContentType</param-name>
<param-value>text/html</param-value>
</init-param>

....

<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>

А тепер конфігурація pages-servlet.xml:

<!-- Конфігурація Tiles -->
<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
<property name="definitions">
<list>
<value>/WEB-INF/defs/general.xml</value>
<value>/WEB-INF/defs/secure.xml</value>
</list>
</property>
</bean>

<bean id="viewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
</bean>

<!-- Контролер повертає view = /layout/header -->
<bean name="/layout/header.page" class="au.com.at2.web.spring.HeaderController"/>

Частина файла general.xml, в якому описуються оголошення Tiles:

<definition name="home" extends="default">
<put-attribute name="title" value="Home Page"/>
<put-attribute name="body" value="/templates/home.ftl"/>
</definition>
<definition name="default" template="/templates/main.ftl">
<put-attribute name="header" value="/layout/header.page"/>
<put-attribute name="footer" value="/templates/layout/footer.ftl"/>
</definition>
<definition name="/layout/header" template="/templates/layout/header.ftl"/>

Невеличке пояснення:
Спочатку добавив новий сервлет для FreeMarker'а. Всі шаблони в мене наразі стоять в каталозі templates, в той час, як TemplatePath для FreeMarker'а - це /. Я це зробив собі на майбутнє - для добавлення нових каталогів крім templates, - адже аплікація має розширюватися (вони завжди розширюються).
В pages-servlet.xml добавив bean tilesConfigurer, який будує конфігурацію для Tiles. Оголошення же знаходяться у файлах /WEB-INF/defs/general.xml, /WEB-INF/defs/secure.xml. При конфігурації інформація із них мерджеться, отже, оголошення default є доступним і в межах secure.xml.


<put-attribute name="header" value="/layout/header.page"/>

Цей кусок значить - можна використовувати як тайл і сторінку, що обробляється DispatcherServlet, таким чинном, ми можемо мати окремі контролери для окреми тайлів. І це правильно. Без цього би було туго. Контролер au.com.at2.web.spring.HeaderController повертає view /layout/header, який оголошений як definition в general.xml.

RichFaces + JSF RI + JBossPortletBridge + Liferay Portal on JBoss AS 4.2.x

The task to make few portlets for Liferay 5.1 Portal using RichFaces + JSF RI looks trivial. But it isn't.
There are few decisions:

  1. Use Liferay listener for Sun JSF with Sun's FacesPortlet.
  2. Use JBossPortletBridge, because it supports RichFaces and AJAX.

I choosed the second with understanding that JBossPortletBridge is in beta and everywhere says that it supports JBossPortal, but I did not find where says - "It supports Liferay too". It does not support Liferay Portal 5.0.
I found few interesting things, that anyone should know before starting write portlets with RichFaces for Liferay using JBossPortletBridge 1.0.x Beta:

  1. RichFaces should be at least 3.2.1.
  2. You should configure portlet as ajaxable with server side state saving policy
  3. Override the javax.portlet.faces.GenericFacesPortlet given with JBossPortletBridge. You'll need manually setup the request attribute javax.servlet.include.path_info with correct viewId you want to process and render. Or you can setup this request attribute at the PortletExternalContextImpl class in calculateServletPath() method (remember JBossPortletBridge 1.0.x Beta).
  4. Use <a4j:commandXXX/> components to process navigation. <h:commandXXX/> will not help ((.


Good luck!



Update:
My friend Andriy has described the solution with details and configuration samples, and you can find his post by the url http://www.liferay.com/web/guest/community/forums/-/message_boards/message/1712557. It really can be useful for you.



I've uploaded the portlet sample prototype, so it's ready and waits for you :)
http://www.esnips.com/doc/de8b5661-fb6d-4fc7-83a3-ed09aa72c695/PortletPrototype

Java 7

Java 7 в процесі написання. Цікаво знати які саме зміни мають попасти в реліз Java7. Тут можна знайти, що саме планувалося в реліз Java 7 , а ось тут - статус нових фіч станом на початок серпня.

Нажаль, closures - далекі від готовності, і зважаючи, що Ніл Гафтер покинув Google і перейшов працювати в Microsoft, є ризик, що closures не увійдуть до Java7. Проте є всі шанси, що switch для рядків, порівняння enum'ів попадуть в реліз. Але от підтримка операторів +, -, *, /, % для BigDecimal - ще далекі від кінця. Хоча це мені видається не менш важливим, ніж інші нововедення.

Для тих, хто не раз відчував дискомфорт при роботі із generics в Java - слід звернути увагу на type literals і type inference.

Invokedynamic - потрібний для підтримки динамічних мов.

String.length(): підводні камні


Як виявилося, не завжди length() повертає дійсну кількість символів у рядку. В класі String чимало підводних камнів та різної недокінця вірної поведінки. Одна із них стосується того, як правильно рахувати кількість символів у рядку.
Тут більше про це...

How to throw unexpected checked exceptions

Today I found that it is possible to throw checked exceptions even if they are not declared in throws section.
What did we do when we have need to throw some checked exception? We do:

public void doSomething() throws CannotDoSomethingException {
throw new CannotDoSomethingException();
}

where

public class CannotDoSomethingException extends Exception {
...
}


But we can to throw the checked exception without declaring it at throws section:

public void doSomething() {
Thread.currenThread().stop(new CannotDoSomethingException());
}


I think that this is veeeery bad code

java.lang.String.concat()

Світ класу java.lang.String є чималим. Багато речей заховано глибоко і далеко, чимало просто плавають на верху, але ми їх не завжди бачимо.

Так, наприклад, метод concat() приймає рядок і повертає рядок, який є конкатенацією даного рядка і переданого у вигляді параметра. Рядок-параметр добавляється в кінець даного рядка. Існує, щоправда, виключення, а саме: якщо рядок-параметр є пустим (себто, ""), то тоді метод concat() повертає дану стрічку. В даному випадку економиться час на виконання операції конкатенації для двух рядків. Це є перша відмінність від використання оператора конкатенації рядків "+".

Іншою різницею є те, що якщо рядок-параметр є null, то тоді java.lang.NullPointerException виникає. У випадку ж з "+", null буде замінено на рядок "null", оскільки, String.valueOf(null) повертає "null".

Assertions

Well, one of the most usable feature of safe codding is assertions.
Assertions help to prevent executing code with usafe or unchecked data, for example, with null or wrong parameters, empty strings, too long strings, empty arrays or lists can be unforeseen arguments and must be checked before using.

You may remember the asserts from JUnit. Remember:

assertNotFalse(a > 5, "a cannot be equal or less then 5");

Well, in Apache Commons Lang library you may use the org.apache.commons.lang.Validate class to validate arguments or state in your code. You may use it next:

void saveEntity(Entity entity) {
Validate.notNull(entity);
}

Then, if entity value is then java.lang.IllegalArgumentException is thrown. In other case next code is executed.

There is also overloaded version of such assert methods in Validate class or in JUnit, that helps to describe the assertion:

void saveEntity(Entity entity) {
Validate.notNull(entity, "Entity cannot be null.");
}

I am the fun of the overloaded version of assert methods. Truly say, I think that only version with string description of assertion has right to live. That helps developers to find the source of problem quickly and also it may be used to process the errors.
The other point is to have assert-liked-validation also for state, that will throw the java.lang.IllegalStateException. This type of assertion should used for checking the result of calculation or processing, but not for arguments. So we could have something like:

void saveEntity(Entity entity) {
ArgumentAssert.isNotNull(entity, "Entity cannot be null.");

// create or update the entity,
// then returns entity id, that must be not null value if everything is OK
Integer entityId = dao.save(entity);

StateAssert.isNotNull(entityId, "Entity is not saved, because entity id is null.");
}

The code is not the best, but this shows how StateAssert should be used.

So, if the entityId is null, then StateAssert.isNotNull() method throws IllegalStateException. Before that, ArgumentAssert.isNotNull() method checks whether entity is not null and throws IllegalArgumentException if it is.


And every assert methods, should check whether assertions are enabled, for example, by using system properties.

And what about assert statement in Java?
It should be used, but one type of assertions support must be choosed.

Build Flex with ANT

Ця стаття про виконання білда простої Flex аплікації із використанням ANT.

Мабуть, для того щоб виконати білд простої Flex аплікації слід знати тільки два тарґети:

  • mxmlc - білд mxml у swf файл.

  • html-wrapper - побудова hmtl файлів обгорток для результуючого swf файла.


Нехай, FLEX_HOME - вказує на шлях, де знаходиться на диску сам Flex, тоді, можна підключити в ANT скріпті

<taskdef resource="flexTasks.tasks" classpath="${FLEX_HOME}/ant/lib/flexTasks.jar"/>

Тоді, для того, щоб збілдити swf'ку слід виконати наступний антівський скрипт:

<mxmlc file="${flexApps.basedir}/src/EventsView.mxml"
context-root="/WebAppContext"
keep-generated-actionscript="true"
services="${webapp.basedir}/WebContent/WEB-INF/flex/services-config.xml"
output="${flexApps.output.dir}/EventsView.swf">
<compiler.library-path dir="${FLEX_HOME}/frameworks" append="true">
<include name="${ipcom.basedir}/WebContent/WEB-INF/lib/"/>
</compiler.library-path>
<load-config filename="${FLEX_HOME}/frameworks/flex-config.xml"/>
<source-path path-element="${FLEX_HOME}/frameworks"/>
</mxmlc>


Отже, що собою являють атрибути:

  • file - файл mxml, який представляє собою флекс-аплікацію

  • context-root - назва контексту веб-аплікації, яка використовуватиметься для загрузки флек-аплікаціїю

  • services - файл із описаними сервісами.

  • output - шлях і назва результуючого файла.



А щоб згенерити обгортки:

<html-wrapper file="EventsView.html"
history="true"
template="express-installation"
application="EventsView"
title="Flex Events Viewer"
width="600" height="400"
output="${flexApps.output.dir}"
swf="EventsView"/>

Функціональне програмування

Мабуть важко не замітити, якої потуги набирає функціональне програмування останнім часом. І тому є декілька поважних причин, а саме:

  • використання функціональних мов програмування для написання програм, що використовують конкурентність; іншими словами фукнціональні мови програмування дозволяють писати програми, що можуть працювати і використовувати всі можливості сьогоднішніх багатоядерних процесорів;

  • гнучкісь функціональних мов програмування просто вражає;

  • написання програм за допомогою функціональних мов є досить нескладним, адже вони беруть основу з математичного визначення функції;

  • декотрі функціональні мови програмування (наприклад, Scala) дозволяє використовувати аспекти об'єктно-орієнтованого програмування.

Доречі, від Apache Commons є бібліотека Functor, яка дозволяє використати деякі елементи функціонального програмування в Java. Нажаль, бібліотека ще не в релізі, але будемо надіятися це не надовго. Ця бібліотека дозволить використати такі корисні речі як 1) функції вищого порядку, 2) фільтри, 3) callback функції тощо.


Більш детальнішу інформацію можна знайти там:

  1. Functional programming in the Java language

  2. Functional programming

  3. Functional Programming in the Real World

Правила "Не забудь!"


  1. при добавленні нових полів до класу звернути увагу на методи hashCode(), equals() та compareTo(), - може слід добавити і туди обробку нового поля

  2. при добавленні поля типу колекції, - надати їй початкове значення

  3. перечитати той код, що ти написав

  4. протестувати те, що ти закодував чи перекодував

  5. може це можна було зробити по інакшому? якщо так, то як? а чим воно краще?

Java call stack for simple web application

Interesting to see the call stack of simple action on web application written on Java, isn't it?
I found the blog story with picture of call stack from HTTP to JDBC. Here it is..

It is beautiful. A long path and only two calls to the application business and DAO methods. Comments are very interesting too.
They are from PHP developers, from administrators etc.

Зміни в блозі

В результаті вивчення суті блогів, статистики блогу та власних побажань, я міняю суть даного блога із суто джавового на суто компютерний із основною орієнтацією на джаву.

Також частина блогів буде на англійській мові. Для чого це потрібно? Мабуть, для того, щоб потренуватися писати статті на англійській мові. Знадобиться ))

BlazeDS: фільтрування повідомлень, що передаються клієнтові

Останні два тижні знашовся час трохи побавитися із Flex та BlazeDS. Основна задача - знайти способи передачі повідомлень від сервера до клієнта, так, щоб клієнт отримував тільки повідомлення, на які він підписався.
Задача - розглянути можливі варіанти. Можливі варіанти були вибрані, а саме:

  • Subtopics

  • Selectors

  • Adaptive polling

  • Dynamic destinations


Може є більше, але це ті, до яких я добрався і з якими я погрався.

Взагалі то, subtopics та selectors можна би було об'єднати, от тільки між ними є невеличка різниця. Використовуючи selectors, можна задати більш гнучку умову вибору повідомлень, які повинень отримувати consumer. Але зрешту, є така річ як MultiTopicsConsumer. Перевірка на те, чи слід віправляти повідомленння клієнту в залежності від підписки на сабтопік чи вибірку по хедерах, виконується на стороні сервера. Іншими словами, до клієнта приходять тільки окремі повідомлення із тих, що вислані на підписаний ним destination. Клієнт отримує повідомлення, що задовільняють необхідну умову.

Adaptive polling дозволяє розширювати функціональність BlazeDS, що відповідає за відправлення повідомлення клієнтові. Тут також слід правильно вибрати, який саме із двох презавантажених методів використовувати у вашому випадку.
Цей метод, дозволяє виконувати необхідну вам дію на повідомленнями, що передаються клієнтові. Тут ми і можемо виконувати гнучку і складну вибірку повідомлень, що ми будемо відправляти клієнтові.

За останнім способом (dynamic destinations) я виконував динамічне створення окремого destination на ту чи іншу розсилку в процесі роботи сервера. А клієнти вже підписувалися на динамічну розсилку. Явні мінуси: це ресурси на створення нової підписки (які не є вже аж такими великими, щоб вважати це проблемою), а також необхідність відстежувати життя підписки та знищувати її якщо вона вже непотрібна.

Новини: 07 червня


  • В реліз вийшла нова версія Spring Security під номером 2.0.2. Детальніше...

OutOfMemoryError: PermGen

Десь кілька місяців тому виявилося, що в проекті, над яким я працюю, знайшлася потворна бага. Відловити її було неможливо. В будь-який момент працювати з програмою ставало неможливо. В логах вивалювалася помилка OutOfMemoryError: PermGen.

Звичайно, я з цим не вперший раз стикаюся, і вирішив спробувати старий добрий спосіб - прописати параметр до Джава машини -XX:MaxPermSize. Отже, прописавши параметр -XX:MaxPerSize=256m, я запустив JBoss. І що? А нічого. Все одно програма вивалювалася... Так само непердбачувано і неочікувано :(.

Тоді я вирішив спробувати ще раз, але цього разу збільшив память для PermGen space вдвічі: -XX:MaxPerSize=512m. Особливо це мені не допомогло, добитися впевнених 24/7 так і невийшло. І було вирішено, що слід ритися вглубь, не виправляти проблему, а викорінити причину!
Порившись в інтернеті я надибав блог by Frank Kieviet, в якому розповідалося про використання утиліт для Java 6: jmap та jhat.

jmap - дозволяє створювати дампи пам'яті Java програми.
jhat - дозволяє представити інформацію дампа у зручному для користувача вигляді.

Отож, використовуючи jmap я зробив дамп heap пам'яті запущеної програми в процесі її роботи. Я виконав команду

>jmap -dump:format=b,file=dump1 3412

цим самим вказавши, що слід виконати дамп пам'яті програми, зберегти її в бінарному форматі у файлі dump1. 3412 - це pid Java програми, дамп пам'яті якої я робив. Для того, щоб отримати ідентифікатор процесу, у Windows введіть команду tasklist і знайдіть відповідний java.exe процес.

Після того, як в мене вже був збережений у файлі дамп, я скористався програмою jhat. Команда:

>jhat dump1

проаналізувала дамп і запустила веб-сервер, який по-замовчуванню слухає запити на порті 7000. Якщо потрібно буде одночасно запустити декілька jhat, то знайте - адрес порта можна змінити відповідним параметром. Слід також звернути увагу на параметр -J. Всі параметри, які задаються після -J потрапляються напряму до Java машини. В моєму випадку це знадобилося для того, щоб збільшити максимальну межу пам'яті, яку можна виділити. У випадку програми, яка працювала під JBoss AS 4.0.4, я змушений був запускати jhat компандою
>jhat -J-Xmx1536m dump

Після старту jhat, можна спокійно відкривати ваш олюблений броузер, і вводити адрес http://localhost:7000/. Тепер можна переглядати дамп пам'яті програми, використовуючи набір функціоналу, який надається програмою.

Скориставшися цією програмою, я визначив причину моєї проблеми – не всі динамічно генеровані класи вигружалися; декотрі з них так і залишалися назавжди в пам'яті. Оскільки, PermGen Space (Permananent Generated Space) - це простір в пам'яті, в якому знаходяться довгоживучі (або вічноживучі в межах запущеної java-машини) об'єкти, а серед таких є класи та 'interned strings', то зрозуміло, що суть моєї проблеми в тому, що декотрі динамічно генеровані класи продовжували генеруватися, завантажуватися в пам'ять, але навідмінно від інших, вони залишалися там навічно. Рано чи пізно вільна пам'ять завершувалася, і програма уходила в аут із знойним повідомленням "OutOfMemoryError: PermGen".

Але лікування цієї проблеми це вже зовсім інша історія... І наразі вона ще не закінчилася :(.

Реліз Spring WebFlow 2.0

15 травня, Spring Framework анонсувала вихід в реліз WebFlow версії 2.0. Серед проголошених основних фіч - це "першокласна" підтримка Java Server Faces (Spring Faces), AJAX (Spring JavaScript).

Spring WebFlow використовується як доповнення до вже існуючих веб-фреймворків (наприклад, Spring MVC, JSF).
Даний фреймворк дозволяє представити веб аплікацію у вигляді flow'ів, кожен з яких містить набір станів та переходів між станами. Кожний такий flow можна уявити собі вигляді діаграми станів UML. Практично, діаграма станів використовується для того, щоб графічно змоделювати або показати набір станів в межах flow. Це є прекрасний спосіб вирішення навігації та роботи із станами в межах веб аплікації.

"Implementation Patterns" by Kent Beck


В дорозі до Львову і назад я все-таки знайшов час, щоб дочитати до кінця книгу Кента Бека (Kent Beck) "Implementation Patterns". Дана книжка є таким собі збірником паттернів по написанню коду. В дечому вона перехрещується із всім відомою книгою "Code Complete". Книга орієнтована на Java програмістів; приклади в книзі приводяться на Java, розглядається навіть Collection API, виникає навіть деколи відчуття, що декотрі шаблони підточені під Java.

Взагалі, книжка має досить просту і зручну структуру. Ця стуктура позволяє вам перечитати книжку від корочки до корочки, а також користуватися як каталогом шаблонів.

В ній можна знайти такі розділи:

  1. Theory of Programming розказує про основні цінності та принципи створюваних програм. Виділяється 3 цінності ПЗ, що слід досягати – це взаємодія, простота та розширюваність.

  2. Class - біля 17 шаблонів (типів) класів, що зустрічатимуться програмісту при розробці програм. Цей розділ описує, які класи можуть бути, як слід виділяти та створювати класи та інтерфейси.

  3. State. Такі шаблони, як Direct Access, Indirect Access, Variable, Field, Var Args, Parameter та ще коло 20 патернів. Ці шаблони описують стан (дані) класу, як правильно організовувати стан, як правильно оформлювати доступ, ініціалізацію тощо.

  4. Behavior. Ще 14 шаблонів повязаних із поведінкою. У доповненні до опису шаблонів пов'язаних із станом класів, подаються також шаблони поведінки, роботи із даними. Розглядаються потоки виконання програм і шаблони повідомлень, якими обмінюються класи.

  5. Methods. Ну і як без методів. Цій частині виділяється окремий розділ, який описує такі шаблони, як Factory Method, Helper Method, Query Method, Method Object, Creation, Composed Method etc. Я в свою чергу звернув увагу, на Method Object: перечитуючи його, я згадував усі ті рази коли він міг мені знадобитися.

  6. Collections. Автор звертає нашу увагу на бібліотеку колекцій, що існує в Java SE. Доречі, мене зацікавили графіки-порівняння залежності часу виконання операцій від кількості елементів для різних реалізацій контейнерів.

  7. Evolving Frameworks. Окремий розділ, присвячений фреймоворкам.


Книжка має всього 176 сторінок та опис понад 100 різних шаблонів. В цілому, книга дуже цікава! Підхід автора до постановки проблеми та висвітлення рішення мені особисто сподобався. Не буду казати, що ця книга must read, але якщо прочитаєте, то і самі все зрозумієте ;).

Spring 2.5: використання аннотацій

Про те, що вийшов Spring Framework (www.springframework.org) версії 2.5, я знав вже давно (відносно давно звичайно). Читав про нові можливості даної версії, пробував навіть щось трохи. Але то все було просто так, для себе. Недавно появилася можливість побавитися із Spring 2.5 більш глибше. І я з радістю нею скористався.
Основним моментом, який був для мене цікавим - це використання анотацій у доповнення до XML конфігурації. Саме про це я вирішив трохи написати.

Головним питанням було -- навіщо анотації?
Іншими цікавими питаннями були:
  1. які анотації можна використовувати?
  2. як їх використовувати?
  3. чи можливо працювати і дальше без них?
  4. як можна включати і виключати використання анотацій?
і багато ще інших питань, які виникали по ходу справи.

Отож, про все по порядку.
Анотації не підтримуються по замовчуванню. Для того, щоб їх використовувати, потрібно вказати Spring, що ви хочете працювати і з ними тоже. Для цьому слід добавити у ваш spring.xml наступний рядок:

<context:annotation-config/>.

Namespace context підключається так

xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation=" http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd"

Після цього, в нас появляється можливість використовувати анотації.
А тепер детальніше про самі анотанції. Так ми вже можемо використовувати їх, але які вони є, навіщо вони використовуються?
Найбільш поширеною видається анотації @Autowired. Дана анотація, вказує що в контейнер Spring повинен виконати автоматичне встановлення значення. Дану анотацію можна використовувати для автоматичного встановлення значення в конструкторі, для поля класу, для сеттера. Наприклад:

public class DocumentManager {
@Autowired
private DocumentFinder documentFinder;
private DocumentDao documentDao;
private Document documentPrototype;

@Autowired
public DocumentManager(DocumentDao documentDao) {
this.documentDao = documentDao;
}

@Autowired
public void setPrototype(Document document) {
documentPrototype = prototype;
}
}

В даному випадку автозаповнення за допомогою анотації @Autowired, Spring буде виконувати на основі типів полів чи аргументів. Але виникає проблема, коли, наприклад, у вас може бути більше одного біна із типом DocumentFinder або Document. Що робити тоді, як Spring має знати яке саме значення слід вставити в певне поле? І саме тут вступає у гру анотація @Qualifier.
Анотація @Qualifier дозволяє вказати, який саме container-managed bean використовувати як значення для заповнення. Дану анотацію можна використовувати як для полів класу, так і для параметрів конструктора або інших методів, так само і для сеттерів. Назва біна, який повинен бути вставленим як значення поля чи параметра вказується як value анотації @Qualifier. Доволі просто і зручно, самі подивіться на код:

public class DocumentManager {

@Autowired
@Qualifier("defaultDocumentFinder")
private DocumentFinder documentFinder;

private DocumentDao documentDao;

private Document documentPrototype;

@Autowired
public DocumentManager(@Qualifier("hibernateDocumentDao")DocumentDao documentDao) {
this.documentDao = documentDao;
}

@Autowired
public void setPrototype(@Qualifier("prototypeDocument")Document document) {
documentPrototype = prototype;
}
}

Доречі, Spring дозволяє за допомогою XML вказати явно, який qualifier у певного bean. Для цього використовується піделемент <qualifier> елемента <bean>. Він має такі атрибути, як:
- type - тип анотації, що використовується (про це пізніше)
- value - це значення унікальне для даного біна.

Наприклад, якщо в коді вказано:
  
@Autowired
@Qualifier("prototypeDocument")
public void setPrototype(Document document) {
documentPrototype = prototype;
}

, то в XML

<bean class="Document">
<qualifier value="prototypeDocument"/>
</bean>

<bean class="Document">
<qualifier value="privateDocument"/>
</bean>

І як ви вже певно зрозуміли, при автозаповнення буде використаний перший bean, тому що його qualifier value є prototypeDocument.

Але це ще не всі можливості, які надає Spring у питанні вирішення, який саме bean повинен бути використаний для автозаповнення. Так, наприклад, ви можете створювати власні анотації, які використовувати замість @Qualifier.
Наприклад:

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface DocumentFinder {
String documentType();
String finderScope();
}

тоді

@Autowired
@DocumentFinder(documentType="MSWord", finderScope="local")
private DocumentFinder documentFinder;
а в XML

<bean class="DocumentFinder">
<qualifier type="annotation.DocumentFinder">
<attribute name="documentType" value="MSWord"/>
<attribute name="finderScope" value="local"/>
</qualifier>
</bean>

І це ще навіть не всі можливості...
Так, наприклад, я не розказав про @Resource. А також і не згадав досі про можливість використання компонентів, які оголошенні за допомогою анотацій.

Але про це все або читайте в туторіалі від Spring або вже у наступній статті.