понеділок, 31 січня 2011 р.

Very good article on Java GC

"Recently, I have been working with a number of customers on JVM tuning exercises.  It seems that there is not widespread knowledge amongst developers and administrators about how garbage collection works, and how the JVM uses memory.  So, I decided to write a very basic introduction and an example that will let you see it happening in real time!" 
Read More 


Также на Хабре выложен перевод: Garbage Collection наглядно

пʼятниця, 21 січня 2011 р.

shellinabox: ajax web-based terminal for your linux


In case you are as lucky as me to work in banking were everything is secured and checked, you may miss access to your home box just as deeply as me. I managed to ssh to my box through corporate proxy one day, but the next day I found it blocked. I was desperate.

Luckily world hates firewalls and loves to innovate. There are quite a handful of web-based consoles available now. I didn't want to spend half a day comparing them so just started with shellinabox (http://code.google.com/p/shellinabox/). It is a web-based AJAX vt100 compatible terminal. It can export command-line tools that have interactive interface of some kind to the web and you can access that from anywere. Wave bye-bye to your PuTTy client, the only thing you need now is a web browser.

The project hosts prebuilt packages for apt-based distros, so you should have no problem in case you use either Ubuntu or Debian. For other distros you'll need to build it from sources. All that stuff can be found here (http://code.google.com/p/shellinabox/downloads/list)

Go grab one and install it with

grim@kepler:~$ wget http://shellinabox.googlecode.com/files/shellinabox_2.10-1_amd64.deb
grim@kepler:~$ sudo dpkg -i shellinabox_2.10-1_amd64.deb


In a few seconds you'll get it running at https://$lt;your-server-address>:4200/.

It has SSL preconfigured out of the box and self-signed certificate is generated for 20 years which should be enough I think. If you need to be 100% secure, you're better export it, carry around with you and import in those browsers you use most, so you could be sure that nobody standing in between you and classified on your box. I've imported the certificate at work and at my home PC for example.

Another thing is that port 4200 was blocked from work too. Bastards! If you hate your security folks just as ultimately as me, I'm sure need to drink some beer with you and share my pain.

What I did I put it behind Apache on the same box, so I could access it just as any other site on the web. Casting some sourcery onto openssl, mod_proxy and shellinabox parameters did the trick and it was nice one. Check it out on the shot!

субота, 15 січня 2011 р.

REST with Spring: Strict resource URLs

Russian translation is posted on my blog at Habrahabr

As you may know Spring MVC is using new annotation driven configuration model since 2.5. To enable it, you should use the <mvc:annotation-driven /> tag in your Spring configuration file..

What it does under the hood is it registers DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdaptor in your application context 

What DefaultAnnotationHandlerMapping does is searches your classes for @RequestMapping annotation and creates mapping for each and on top of that two mappings ending with .* and /.

So once you have following controller:

@Controller
@RequestMapping("/service/hotels")
public class HotelsCollectionController {

    @Autowired
    private HotelService hotelService;

    @RequestMapping(method = RequestMethod.GET)
    public String getHotelList(Model model) {

        List<Hotel> list = hotelService .getHotelList();

        model.addAttribute("hotels", list);

        return "service/hotels/read";
    }

    public void setHotelService(HotelService hotelService) {
        this.hotelService = hotelService;
    }
}

you will get three mappings internally for /service/hotels, /service/hotels/ and /service/hotels.*

The purpose of first two is clearly to be more friendly to the user and the last is used for content negotiation with ContentNegotiatingViewResolver.

And thats fine for most web applications. 

The problem arises when you try to apply RESTful approach to your web services using annotations for mapping. Since URL is now effectively a resource, different URLs are now different resources and your application should not be vague about interpreting it.

Another point is that Spring itself treats these implied URLs without too much care by default.

Naive me configured DefaultAnnotationHandlerMapping for application and set its defaultSuffixPattern property to false. And if that would be as simple as that, there was no much sense in this post.

Straightforward solves the problem at first site 

    <mvc:annotation-driven />

    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
        <property name="useDefaultSuffixPattern" value="false" />
    </bean>

Now think about it. Once Spring sees <mvc:annotation-driven /> it creates one DefaultAnnotationHandlerMapping and places it in the context. Once it sees the above bean definition it creates *another* instance of DefaultAnnotationHandlerMapping and also places it in the context. So your happy app will have two HandlerMappings, one with default settings and another  configured one. Now which HandlerMapper will happen to chew the HTTP request first depends on the internal order of two, and is completely out of your control (well, almost... you can apply ordering, but that's kinda hack and besides trashes the story).

While for /service/hotels there is no difference, for /service/hotels/ and /service/hotels.* there is. Chances are that you will be using ContentNegotiatingViewResolver to negotiate best representation of resource for client and in this case you've effectively lost control on resolving the correct View, resulting from incorrect view being returned and ending with 500 on server. I hope I'll find time to gather the details later.

To avoid this, you should remove one of HandlerMappings from context. So we should remove <mvc:annotation-driven /> completely and do the hard work of registering AnnotationMethodHandlerAdaptor.

    <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
        <property name="useDefaultSuffixPattern" value="false" />
    </bean>

    <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" />

This should do what is required and you will get desired 404 on implicit URLs. 

четвер, 6 січня 2011 р.

Создание своих архетипов и каталогов в Maven

Уже довольно продолжительное время (около года набежит) активно пользуюсь системой сборки Apache Maven и вполне ею доволен. Несмотря на свои очевидные и не очень недостатки, неоспоримым преимуществом является автоматическое управление зависимостями, хорошая структурированность проектов и отсутствие скриптов сборки как таковых, а следовательно проблем с ними.

Многим может не нравится, что мавен в самом деле отбирает у разработчика свободу выбора структуры проекта и прямо таки диктует ее, но в самом ли деле эта свобода настолько важна, чтобы делать изза нее жирный аргумент против? Не думаю. Есть другие, более серьезные, на мой взгляд, недостатки, в первую очередь - трудность диагностики проблем при сборке и недостаточная документированность мавена и плагинов.

Был случай на проекте, когда новый член команды не смог собрать билд, получив маловразумительный вывод, что и только после двух часов копания выяснилось, что в поме была прибита пятая версия явы как основная, а она не была установлена.

Поиск плгинов и их настройка - это тоже мучительные круги ада, но по сравнению с xml-программированием на ant'e это еще ничего.

Однако при правильном подходе и набитом скилле, мавен практически не ощущаетсяв работе, что мне очень нравится.
Создаем локальный каталог

Собственно, в этой статье речь пойдет не о муках, а о архетипах, их каталогах и создании собственных архетипов и каталогов.

Архетип в мавене - это шаблон нового проекта, со структурой и заготовками исходных и конфигурационных файлов.

Любой, кто хотьябы раз создавал проект на мавене, сталкивался с архетипами. Например, типичный метод создания проекта, который витает в интернете:

mvn archetype:create                                    \
  -DarchetypeGroupId=<archetype-groupId>                \
  -DarchetypeArtifactId=<archetype-artifactId>          \
  -DarchetypeVersion=<archetype-version>                \
  -DgroupId=<my.groupid>                                \
  -DartifactId=<my-artifactId>

Вопрос, откуда взять нужные параметры, всегда меня волновал, обычно я вежливо спрашиваю у Гугла и он мне обычно отвечает :)

Если мы например, захотим создать простоe приложение, то мы используем архетип под названием maven-archetype-quickstart, например вот так:

mvn archetype:create                                    \
  -DarchetypeGroupId=org.apache.maven.archetypes        \
  -DarchetypeArtifactId=maven-archetype-quickstart      \
  -DarchetypeVersion=1.0                                \
  -DgroupId=org.example                                 \
  -DartifactId=simpleapp

Есть более удобный способ создания проекта, с помощью цели archetype:generate. При вызове, в интерактивном режиме будет предложено ввести параметры нового проекта.

mvn archetype:generate

Но тут возникает проблема. Мавен предлагает выбрать тип нового проекта из списка готовых шаблонов, а список состоит ни много ни мало из более чем 300 вариантов. Найти там нужный шаблон -- задача довольно нетривиальная, обычно я сбрасываю вывод в файл и потом грепом ищу то что нужно.

Погуглив немного, я нашел для себя решение этой проблемы, не совсем как по мне окончательное, но вполне себе с намеком на элегантность. Архетипы можно обьединяются в каталоги! Но как это может помочь?

Каталог определяется URL'ом, где он расположен, кроме того в мавене есть три предопределенных каталога, или если хотите, алиаса: internal, remote и local.

internalсодержит архетипы, встроенные в maven, их немного и они уже де-факто идут с самим дистрибутивом
remoteцентральный каталог maven, находится по адресу http://repo1.maven.org/maven2/archetype-catalog.xml, его местоположение зависит от текущих настроек мавена, например возможно переопределить этот урл на одно из зеркал репозитория
localкаталог из локального репозитория, обычно находится в ~/.m2/archetype-catalog.xml

У цели archetype:generate есть параметр archetypeCatalog, с помощью которого можно указать список каталогов, где нужно искать возможные архетипы. По умолчанию, значение параметра 'remote,local'. Но если убрать оттуда remote, то получим почти то, что нужно.

Например, вот так:

grim@blackbox:~/projects$ mvn archetype:generate -DarchetypeCatalog=local

[ ...булшит... ]

Choose archetype:
1: local -> maven-archetype-quickstart (quickstart)
2: local -> maven-archetype-archetype (archetype)
3: local -> maven-archetype-webapp (webapp)
Choose a number: 1:

Подсмотреть структуру файла можно из каталога remote. Например, чтобы получить список выше, файл должен иметь вид

<?xml version="1.0" encoding="UTF-8"?>
<archetype-catalog xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0 http://maven.apache.org/xsd/archetype-catalog-1.0.0.xsd"
    xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype-catalog/1.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <archetypes>
    <archetype>
      <groupId>org.apache.maven.archetypes</groupId>
      <artifactId>maven-archetype-quickstart</artifactId>
      <version>1.1</version>
      <description>quickstart</description>
    </archetype>
    <archetype>
      <groupId>org.apache.maven.archetypes</groupId>
      <artifactId>maven-archetype-archetype</artifactId>
      <version>1.0</version>
      <description>archetype</description>
    </archetype>
    <archetype>
      <groupId>org.apache.maven.archetypes</groupId>
      <artifactId>maven-archetype-webapp</artifactId>
      <version>1.0</version>
      <description>webapp</description>
    </archetype>
  </archetypes>
</archetype-catalog>


Немного поколдовав с конфигурацией мавена, можно сделать такую ситуацию постоянной, нужно создать профиль, в котором выставить значение переменной archetypeCatalog. Для этого в ваш файл settings.xml нужно добавить

  <profiles>

    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
           <archetypeCatalog>local</archetypeCatalog>
        </properties>
    </profile>

  </profiles>

Усе! Теперь, при вызове, цель archetype:generate вместо тонн мусора, будет выводить вам то, что скажете.

К слову у плагина archetype есть любопытная цель crawl, которая сканирует ваш локальный репозиторий на предмет наличия архетипов и генерирует из всех найденных каталог. К сожалению, документация немного врет и по умолчанию файл генерируется совсем не там где ожидает увидеть его мавен. Готовить нужно так:

grim@blackbox:~/projects$ mvn archetype:crawl -Dcatalog=~/.m2/archetype-catalog.xml

Вопреки официальной документации параметр называется не catalogFile, а catalog.

Создаем свой архетип

Вторая проблема, которую мы рассмотрим - это создание собственного архетипа. Зачем это нужно? Нужно это по той простой причине, что при всем кажущемся обилии архетипов, среди них не оказалось подходящего для простой веб-разработки. Есть несколько близких по духу, например тот же maven-archetype-webapp, но дескрипторы в нем устаревшие, нету log4j и нормального темплейта jsp. В итогое после создания пустого проекта, нужно перейти в режим работы напильником, и с помощью гугла переделывать все как надо. В конце концов на десятый раз мне это надоело, и я решил создать свой собственный архетип, самый лучший и самый правильный. Более конкретно, создадим заготовку для простенького приложения на spring-mvc c использованием Servlet 2.5/JSP 2.1/JSTL 1.2 c готовым к работе логированием.

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

Для начала нужно создать проект для нашего архетипа, используя архетип maven-archetype-archetype, например с помощью того же archetype:сreate.

grim@blackbox:~/projects$ mvn archetype:create      \
   -DarchetypeArtifactId=maven-archetype-archetype  \
   -DartifactId=baremvc                             \
   -DgroupId=example

Чтобы не мучится в консоли, пересядем в эклипс, хотя конечно же все, что написано ниже, можно сделать и с помощью vim'а или еще чегото такого.
grim@blackbox:~/projects$ cd baremvc && mvn eclipe:eclipse

После чего делаем импорт и смотрим на структуру более детально.

 

Проект состоит из одной папки resources, в которой содержится две подпапки: archetype-resources и META-INF. В первой храниться костяк будущих выдающихся проектов, которые еще будут созданы поколениями програмистов после нашей смерти, во второй хранится файлик META-INF/maven/archetype.xml. Это дексриптор архетипа. В нем будет хранится описание того, что входит в архетип.

Дополним костяк, всем чем нужно: добавим туда простенькую jsp страничку, более-менее сносный web.xml, простенький контроллер и конфиг log4j.properties. Все это вы найдете в архиве, который можно скачать. Из интересных моментов, на которые стоит обратить внимание, это замены, которые делает мавен при создании проекта. В архетипе реализован механизм темплейтов на основе Velocity, который практически не документирован, если ктото уверен в обратном, поделитесь, буду благодарен.

В частности в контроллере через темплейты реализована подстановка имени пакета.

// HomeController.java
package $package;

import org.apache.commons.logging.Log;

@Controller
public class HomeController {

...


Тот же самый ход использован в конфигурации Spring MVC и при генерации целевого pom.xml. Пока что список известных мне переменных довольно скуден: $groupId, $artifactId, $version и $package. Думаю, всем понятно, что каждый значит, все это указывается при создании проекта из архетипа.

Вообще тема темплейтов интересная и нераскрытая, думаю немного погодя раскопаю ее поглубже, а пока то что есть, вполне достаточно для наших скромных нужд.

Когда процесс подготовки шаблонов завершен, нужно подготовить файл-дескриптор архетипа, тот самый archetype.xml.

<archetype xmlns="http://maven.apache.org/plugins/maven-archetype-plugin/archetype/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/plugins/maven-archetype-plugin/archetype/1.0.0 http://maven.apache.org/xsd/archetype-1.0.0.xsd">

  <id>baremvc</id>
  <sources>
    <source>src/main/java/HomeController.java</source>
  </sources>
  <resources>
      <resource>src/main/resources/log4j.properties</resource>
      <resource>src/main/webapp/home.jsp</resource>
      <resource>src/main/webapp/WEB-INF/applicationContext.xml</resource>
      <resource>src/main/webapp/WEB-INF/web.xml</resource>
  </resources>
  <testSources />
</archetype>   

Тэг <id></id> должен совпадать с artifactId нашего архетипа. Тэги <sources> и <resources> прдназначены для темплейтов разных частей архетипа. В частности ресурсы из папки webapp нужно указывать в тэге <resources>. Есть еще несколько допустимых секций. Ниже перечислены допустимые секции и папки, куда уйдут указанные в них шаблоны.
  • <sources> => src/main/java
  • <resources> => src/main/resources
  • <testSources> => src/test/java
  • <testResources> => src/test/resources
  • <siteResources> => src/site


После того как все готово, можно установить наш архетип в репозиторий с помощью

grim@blackbox:~/projects/baremvc$ mvn clean install

и теперь он готов к использованию.

grim@blackbox:~/projects/baremvc$ cd ..
grim@blackbox:~/projects$ mvn archetype:create          \
  -DarchetypeGroupId=org.example                        \
  -DarchetypeArtifactId=baremvc                         \
  -DarchetypeVersion=1.0                                \
  -DgroupId=org.example                                 \
  -DartifactId=baremvcapp

Наш маленький проект готов к запуску

grim@blackbox:~/projects/baremvcapp$ cd baremvcapp
grim@blackbox:~/projects/baremvcapp$ mvn tomcat:run

После чего по ссылке http://localhost:8080/baremvcapp можно узреть сие творение.

Теперь архетип, можно сказать приготовлен, добавим его в локальный каталог руками или с помощью цели archetype:crawl.

grim@blackbox:~/projects$ mvn archetype:crawl -Dcatalog=/home/grim/.m2/archetype-catalog.xml

Теперь при вызове archetype:generate в списке должна появиться строчка

1: local -> baremvc (baremvc)

с чем я нас и поздравляю.

В заключение осталось добавить, что описанный выше способ устарел (увы), и помечен как depreceted, но все еще работает, а про right way документации как то кот наплакал. Все что мне пока известно, это что дескриптор изменили, теперь он называется archetype-metadata.xml и имеет более мощный синтаксис. Надеюсь хватит еще на десяток лет вперед статьи писать.



Архив, который можно скачать


Исходный код, который можно посмотреть

Использованные ресурсы

  1. Guide to Creating Archetypes - http://maven.apache.org/plugins/maven-archetype-plugin-1.0-alpha-7/examples/archetype.html
  2. Maven Archetype Plugin - http://maven.apache.org/archetype/maven-archetype-plugin/
  3. Maven – размышления после двух лет использования - http://habrahabr.ru/blogs/personal/102181/