вторник, 29 декабря 2009 г.

Запуск "Наемники : Карты судьбы"

Не так давно наступил день, который я так долго ждал. 25 декабря был запущен проект для ВКонтакте - игра "Наемники : Карты судьбы", над которым я трудился последние 2,5 месяца. Серверная часть проекта была реализована полностью мной, что называется "своими руками". Давненько мечтал о таком маленьком проекте, в котором я смогу применить все свои знания и опыт в области разработки.

Использовались следующие java-технологии:

  • Spring
  • Spring security
  • Spring BlazeDS Integration
  • BlazeDS
  • Hibernate
  • JBoss Cache
  • Unitils
  • Jmockit
  • DBUnit
  • TestNG

Думаю, что некоторые из вас скажут, что с использованием Hibernate хорошей производительности не добьешься, но я с этим не соглашусь. Да, в процессе разработки в Hibernate нашлись некоторые баги и выявилось весьма "необычное" поведение, но все эти проблемы можно решить.

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

Отмечу также и тот факт, что более 80% кода было покрыто тестами. Хотя этот процесс и занимал у меня до 50% времени, я считаю, что именно благодаря тестам удалось выйти в релиз с минимальным кол-вом багов на серверной стороне. Без тестов был бы невозможен безболезненный рефакторинг и быстрое добавление новой функциональности.

среда, 28 октября 2009 г.

Динамическая модификация контекста в Spring

Существует механизм изменения контекста Spring, который запускается после сбора всей информации о нем (из xml и аннотаций) и срабатывает до создания самих bean-ов. Я говорю об интерфейсе org.springframework.beans.factory.config.BeanFactoryPostProcessor, реализуя который вы получаете именно такую возможность. Используя данный механизм очень удобно менять параметры уже объявленных где-то в контексте bean-ов. Объект реализующий этот интерфейс можно зарегистрировать как обычный bean в контексте, либо добавить его на этапе создания контекста, например так:

Только не забудьте указать в конструкторе контекста аргументу refresh значение false, иначе наш механизм будет вызываться лишь для новых bean-ов добавляемых уж после создания контекста (как это делать я писал тут). При вызове метода refresh произойдет инициализация контекста и наш обработчик будет вызван.

Описанный выше прием оказывается очень полезен в использовании при тестировании, когда нужно изменить какие-то из параметров bean-ов или просто выставить свои значения.

понедельник, 17 августа 2009 г.

Управление версиями в Maven

После очередного релиза проекта приходится менять его версию, проходясь при этом по всем pom-файлам. Процедура эта довольно утомительная и избавить от нее поможет versions-maven-plugin.

Достаточно выставить в головном pom-файле новую версию проекта и затем выполнить команду:

mvn versions:update-child-modules

Как вы наверное догадались, команда выполнит обновление всех версий проекта в pom-файлах дочерних модулей. Если же, по какой-то причине, вы остались недовольны проделанной работой плагина, то содержимое pom-файлов можно легко вернуть в исходное состояние командой:

mvn versions:revert

Чтобы удалить backup-ы pom-файлов выполните:

mvn versions:commit

Помимо выполнения обновления версии проекта, есть еще несколько полезных возможностей. Вывод списка новых версий зависимостей проекта, доступных из репозитария:

mvn versions:display-dependency-updates

Список новых версий плагинов используемых в проекте:

mvn versions:display-plugin-updates

суббота, 8 августа 2009 г.

Параметры производительности nginx и tapestry 5

Думаю, что вам известно о HTTP-сервере nginx, а также возможности его использования в качестве front-end сервера для передачи разных ресурсов клиенту. Использование nginx позволяет разгрузить ваш сервер Java-приложений от лишней работы со стататическими ресурсами.

Отмечу лишь несколько основных, на мой взгляд параметров, влияющих на производительность системы использующей Nginx и веб-фреймворка Tapestry 5.

Параметры для nginx, выставляемые в nginx.conf:

  • worker_processes - кол-во процессов, обеспечивающих загрузку ресурсов с диска. Значение зависит от объема ресурсов и скорости носителя с которого производится чтение. Также играет роль число ядер/процессоров в вашем сервере, думаю, что значение должно быть не меньше чем их кол-во;

  • worker_connections - максимальное кол-во одновременных соединений с сервером. Следует выбрать оптимальное число, которое сможет держать сам сервер-приложений. Именно верный порог соединений предотвратит DDOS-атаки на ваш сервер и даст уверенность в том, что ваш сервер будет устойчиво работать.

Настройки производительности у Tapestry 5 сводятся к верной настройке пула страниц. Напомню, что при каждом обращении клиента фреймворк вытаскивает из пула экземпляр определенной страницы. Параметры:

  • tapestry.page-pool.hard-limit - максимальное значение страниц в пуле. Следует учесть, количество локалей поддерживаемых вашим приложением, т.к. для каждой локали создается отдельный экземпляр страницы (по-умолчанию значение 20);

  • tapestry.page-pool.soft-limit - кол-во страниц в пуле, после которого tapestry перейдет в ожидание освободившихся страниц. Пока число страниц в пуле не достигнет этого значения и свободных экземпляров не будет, они будут создаваться (по-умолчанию значение 5).

понедельник, 3 августа 2009 г.

Использование LiquiBase с Hibernate и Maven. Часть 2

Теперь о том, что касается изменений которые вносит hibernate в структуру БД, при добавлении/изменении сущностей, если в настройках указан параметр hibernate.hbm2ddl.auto=update. Как продолжить пользоваться этой возможностью, если теперь для всех изменений структуры БД мы должны использовать только liquibase? Было бы не плохо получать sql генерируемый hibernate, который можно легко поместить в changeset liquibase. Для этого необходимо подключить hibernate3-maven-plugin к модулю, в котором используется hibernate:

Необоходимым условием для работы плагина является наличие файла hibernate.hbm.xml, в нем должны содержаться следующие настройки:

Не забудьте также и о том, что теперь все маппинги классов тоже нужно указывать в этом файле, а не в каком-нибудь SessionFactoryBean, если используете Spring. Настройка SessionFactoryBean будет выглядеть примерно так:

Стоит отметить, что параметру hibernate.hbm2ddl.auto, в вашем проекте, нужно присвоить значение validate. Теперь при каждом выполнении команды:

mvn hibernate3:hbm2ddl -Dhibernate.create=true

вам будет выводится sql генерируемый hibernate, который можно вставить в changeset. Параметр -Dhibernate.create=true следует указывать, в том случае, когда вам требуется SQL всей схемы БД. В случае, когда требуется получить SQL-команды, выполняемые hibernate для обновления схемы БД, то вместо первого параметра используйте -Dhibernate.update=true.

И помните, что в случае обновления БД вам нужно будет следить SQL, генерируемым hibernate, т.к. не все изменения параметров объектов входят в SQL (к примеру, hibernate не будет создавать некоторые constraint-ы).

четверг, 9 июля 2009 г.

Использование LiquiBase с Hibernate и Maven. Часть 1

Когда-то я представлял вашему вниманию инструмент для управления изменениями в БД - liquibase. Теперь я решил описать как его можно использовать в проектах с maven-сборкой, и hibernate.

Для начала необходимо создать отдельный maven-артефакт, который будет содержать только ресурсы - xml-файлы с changeSet-ами от liquibase. Структура файлов в папке resources нашего артефакта следующая:

    /changelogs/версия_проекта/*-changelog.xml
/changelogs/версия_проекта/release-changelog.xml
/project-changelogs.xml

Файл project-changesets.xml включает в себя набор файлов release-changelog.xml всех версий проекта, вот пример его содержимого:

Таким же образом файлы release-changelog.xml включают в себя список файлов с измененями, находящихся в той же папке и касающихся лишь данной версии проекта. Сами изменения я предпочитаю описывать не пользуясь при это тэгами liquibase, а использую привычный SQL.

Благодаря наличию в liquibase такого понятия как "контекст", можно разделить все операции над БД на три контекста:

  • schemedata - контекст в котором будут отражены изменения структруры базы;

  • initdata - контекст определяющий данные для инициализации БД, например добавление новых пользовательских ролей;

  • migratedata - контекст используемый при изменении "живых" данных, т.е данных появляющихся в системе при ее эксплуатации.

Такая концепция позволит вам четко разделять виды изменений в БД. А также "накатывать" изменения лишь нужных контекстов. Пример:

Если в проекте предполагается использовать две базы - БД разработчика/продакшн и БД для тестов, то pom-файл, созданного нами артефакта, будет содержать следующие строки:

Параметры app.database.xxx и test.database.xxx можно определить в головном pom-файле проекта. Теперь накатывать изменения на обе БД можно одной командой:

mvn process-resources либо mvn clean install

Чтобы сделать rollback обеих баз на несколько changeset-ов назад к команде следует добавить параметры:

-Dliquibase.goal=rollback -Dliquibase.rollbackCount=1

liquibase.rollbackCount - определят кол-во changeset-ов которые следует откатить.

Порой может возникнуть ситуация, когда требуется внести изменение в существующий changeset и заново накатить все changeset-и. Здесь поможет параметр:

-Dliquibase.dropFirst=true

Если потребуется работать с каждой БД отдельно, то необходимо использовать уже команды самого плагина liquibase:

mvn liquibase:update либо mvn liquibase:rollback

Для тестовой БД добавляем параметр -Ptestdb в командной строке.

суббота, 2 мая 2009 г.

Javascript AOP

Наверное каждому известно что такое АОП или по-просту аспектно-ориентированное программирование. В java есть целый ряд фреймворков реализующих определенные возможности данного подхода.

Но если смотреть в сторону javascript-языка, то возможности АОП в нем реализовать довольно просто:

var AOPUtils = {
addBefore: function(object, methodName, invokedObject, invokedMethodName) {
var oldMethod = object[methodName];
object[methodName] = function() {
var args = $A(arguments);
try {
if (Object.isFunction(invokedObject)) {
invokedObject.call(invokedObject, args);
} else {
invokedObject[invokedMethodName].call(invokedObject, args);
}
} catch (e) {
alert("An exception occurred in method '" + methodName + "' Error: " + e.message);
}
try {
var result = oldMethod.apply(object, args);
} catch (e) {
alert("An exception occurred in method '" + methodName + "' Error: " + e.message);
}
return result;
};
}
};

Представленная функция AOPUtils.addBefore позволяет вам выполнить свою функцию перед функцией какого-либо объекта. Для реализации использовались стандартные javascript-функции, смысл которых состоит в следующем: apply - вызов функции с параметрами в виде массива, call - вызов функции с перечислением параметров через запятую. Первым аргументом обеих функций будет объект который в вызываемой функции будет представлять ссылку на this.

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

var SomeClass = Class.create({
simplemethod: function(url) {
alert(url);
}
});

var sobject = new SomeClass();
AOPUtils.addBefore(sobject, "simplemethod", function(args) {
var url = args[0];
url += "?param=test";
args[0] = url;
});

sobject.simplemethod("http://test.com");

Отмечу, что для использования данного механизма необоходма библиотека Prototype

четверг, 2 апреля 2009 г.

Переключение из HTTPS-соединения в HTTP (в Tomcat)

Для наиболее безопасной передачи конфиденциальных данных по сети, в веб-приложениях, используется HTTPS-соединение. HTTPS-соединение лучше использовать не на весь пользовательский сеанс, а лишь на определенных этапах где защищенность канала действительно очень важна.

К примеру, если сделать на HTTPS-соединении только логин пользователя, а после его входа в систему снова использовать обычный HTTP, то на web-сервере Tomcat придется реализовать поддержку корректного переключения с HTTPS на HTTP.

Конфигурация Spring Security для такого случая:

<security:http auto-config="true" >
<security:intercept-url pattern="/login.html" access="IS_AUTHENTICATED_ANONYMOUSLY"
requires-channel="https"/>
<security:intercept-url pattern="/billing.html" access="ROLE_USER"
requires-channel="http"/>
</security:http>

Возможны два варианта переключения соединений:


1.    2.

В первом случае все будет работать, т.к. Tomcat создаст cookie сперва для HTTP сессии, которая будет действительна и для HTTPS, а следовательно может быть аутентифицирована Spring Security и использована дальше в работе с обычным HTTP.

Во втором случае Tomcat создаст cookie для HTTPS с параметром secure, а это значит что аутентификация такой сессии не позволит использовать в дальнейшем обычную HTTP сессию. Сookie с параметром secure привязана лишь к HTTPS-сессиям. Для пользователя же это обернется редиректом Spring Security на страницу логина, но с уже созданной cookie, и только второй логин уже пустит пользователя в систему. Чтобы решить эту проблему необходимо, находясь в HTTPS сессии, создать cookie для обычной HTTP сессии без параметра secure. С помощью фильтра это реализовать довольно просто:

package com.blogspot.nkoksharov.filter;

import java.io.IOException;

import javax.servlet.*;
import javax.servlet.http.*;

import org.apache.commons.lang.StringUtils;

public class HttpsCookieFilter implements Filter {

@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) request;
final HttpServletResponse httpResponse = (HttpServletResponse) response;
final HttpSession session = httpRequest.getSession(false);

if (request.isSecure() && session != null) {
final Cookie sessionCookie = new Cookie("JSESSIONID", session.getId());
sessionCookie.setMaxAge(-1);
sessionCookie.setSecure(false);
String contextPath = httpRequest.getContextPath();
if (StringUtils.isNotBlank(contextPath)) {
sessionCookie.setPath(contextPath);
} else {
sessionCookie.setPath("/");
}
httpResponse.addCookie(sessionCookie);
}

chain.doFilter(request, response);
}

...

}

Теперь переход с https-соединения будет выглядеть так:

Фильтр лучше всего поместить в самом начале цепочки фильтров, важно чтобы он был перед springSecurityFilterChain. Следует отметить, что при таком решении http-cookie будет приходить пользователю на каждый https-запрос. Т.к. сложно опеределить при https-соединении создана ли cookie для http-канала.

понедельник, 30 марта 2009 г.

Замена механизма отображения ошибок при валидации в Tapestry 5.1

При унифицированном подходе к отображению ошибок на форме, обычно для каждого поля ставится соответствующий html-элемент, либо элементы в которых пользователю сообщается об ошибке в конкретном поле. К примеру, в Spring-mvc в таком случае вам придется ставить тэг form:error для каждого поля.

Механизм отображения ошибок в Tapestry 5 реализован с помощью специального декоратора, который призван избавить разработчика от расставления подобных элементов на своей странице. Реализация этого механизма находится в классе org.apache.tapestry5.internal.DefaultValidationDecorator. Результат его работы выглядит так:

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

Для этого, в модуле tapestry вашего приложения необходимо подменить markup-фильтр DefaultValidationDecorator совоей реализацией:

package com.blogspot.nkoksharov.tapestry;

import org.apache.tapestry5.*;
import org.apache.tapestry5.ioc.*;
import org.apache.tapestry5.services.*;

public class AppModule {

public void contributeMarkupRenderer(OrderedConfiguration configuration,
final Environment environment) {

MarkupRendererFilter defaultValidationDecorator = new MarkupRendererFilter() {
public void renderMarkup(MarkupWriter writer, MarkupRenderer renderer) {
ValidationDecorator decorator = new ExValidationDecorator(environment, writer);
environment.push(ValidationDecorator.class, decorator);
renderer.renderMarkup(writer);
environment.pop(ValidationDecorator.class);
}
};

configuration.override("DefaultValidationDecorator", defaultValidationDecorator);
}
}

Логика декоратора выглядит так:

package com.blogspot.nkoksharov.tapestry;

import org.apache.tapestry5.*;
import org.apache.tapestry5.services.Environment;

public class ExValidationDecorator extends BaseValidationDecorator {

private static final String ERROR_ELEMENT_CLASS = "error_element";

private final Environment environment;
private final MarkupWriter markupWriter;

public ExValidationDecorator(Environment environment, MarkupWriter markupWriter) {
this.environment = environment;
this.markupWriter = markupWriter;
}

@Override
public void afterField(Field field) {
if (inError(field)) {
String errorId = field.getClientId() + "Error";
markupWriter.element("div",
"class", ERROR_ELEMENT_CLASS,
"id", errorId);
markupWriter.write(getErrorMsg(field));
markupWriter.end();
}
}

private String getErrorMsg(Field field) {
ValidationTracker tracker = environment.peekRequired(ValidationTracker.class);
return tracker.getError(field);
}

private boolean inError(Field field) {
ValidationTracker tracker = environment.peekRequired(ValidationTracker.class);
return tracker.inError(field);
}
}

Данный декоратор после каждого поля ставит такой вот элемент:

<div class="error_element" id="someElementError">
Сообщение об ошибке для данного поля
</div>

Стоит отметить, что возможность переопределения элементов в списке конфигурации сервисов, через метод override стала доступна лишь начиная с версии Tapestry 5.1.0.0

воскресенье, 22 февраля 2009 г.

Динамическое добавление bean-ов в контекст Spring

Хорошо известно, что Spring способен создавать bean-ы из статитческого контекста. Однако, имеется возможность динамического создания и регистрации bean-ов, которая в некоторых случаях может оказаться весьма полезной. Такую функциональность предоставляет интерфейс org.springframework.beans.factory.support.BeanDefinitionRegistry метод registerBeanDefinition. Для регистрации вам понадобится объект реализующий интерфейс org.springframework.beans.factory.config.BeanDefinition. Созданием такого объекта занимается класс org.springframework.beans.factory.support.BeanDefinitionBuilder, предоставляющий практически все те же возможности конфигурации bean-а, что и в статическом xml. При регистрации bean-а, если bean c таким id уже существует, то он будет заменен.

package com.blogspot.nkoksharov;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.testng.annotations.Test;

public class SpringTest {

@Test
public void testDynamicCreation() {
ConfigurableApplicationContext context =
new ClassPathXmlApplicationContext("/spring/test/application-context.xml");
BeanDefinitionRegistry factory = (BeanDefinitionRegistry) context.getBeanFactory();

BeanDefinitionBuilder builder =
BeanDefinitionBuilder.rootBeanDefinition(com.blogspot.nkoksharov.TestBeanImpl.class);
builder.addPropertyValue("property", "someValue");
BeanDefinition definition = builder.getBeanDefinition();
factory.registerBeanDefinition("testBean", definition);


...

TestBean bean = (TestBean) context.getBean("testBean");

...

}

}

Также можно и удалять bean-ы методом removeBeanDefinition.

четверг, 19 февраля 2009 г.

Сборка приложения используя maven-assembly-plugin с модулями Spring

Столкнулся с проблемой сборки приложения при помощи maven-assembly-plugin. Напомню, что этот плагин предназначен для создания архива, включающего в себя в распакованном виде какие-либо ресурсы, а также классы из зависимостей проекта. То есть, если вам нужно поместить приложение со всеми его зависимыми классами из других библиотек в один jar-файл, то этот инструмент может вам помочь.

Однако по причине того, что плагин распаковывает все зависимости в один каталог, могу возникнуть проблемы с библиотеками, имеющими файлы с одинаковыми названиями. Именно такая ситуация и происходит, когда пытаешься собрать приложение в один jar имеющее в зависимостях spring библиотеки. После запуска приложения возникает такая ошибка:

Exception in thread "main" java.lang.IllegalStateException: 
org.springframework.beans.factory.parsing.BeanDefinitionParsingException:
Configuration problem: Failed to import bean definitions
from URL location [classpath:/spring/dao-context.xml]
Caused by:
org.springframework.beans.factory.parsing.BeanDefinitionParsingException:
Configuration problem: Unable to locate Spring NamespaceHandler for
XML schema namespace [http://www.springframework.org/schema/tx]
Offending resource: class path resource [spring/dao-context.xml]

at org.springframework.beans.factory.parsing.
FailFastProblemReporter.error(FailFastProblemReporter.java:68)

Возникает она из-за того, что spring не может найти соответствующий NamespaceHandler предназначенный для обработки xml-схемы http://www.springframework.org/schema/tx. Соответствие между NamespaceHandler-ми и xml-схемами описывается в файле spring.handlers. Файл spring.handlers имеется в нескольких библиотеках spring (spring-aop.jar, spring-beans.jar...) в папке META-INF, таким образом в наш jar попадает лишь какой-то определенный экземпляр.При дублировании файлов maven-assembly-plugin в результате оставляет первый из них, merge-ить их он не умеет. Та же ситуация происходит и с файлом spring.schemas, в котором описываются соотвествия путей к xsd описаниям xml-схем и их реальным положением внутри библиотеки spring.

Решить возникшую проблему можно создав файлы spring.schemas и spring.handlers в папке /src/main/resources/META-INF вашего maven-проекта, агрегирующих в себе содержимое одноименных файлов из библиотек. В файле настройки maven-assembly-plugin-а необходимо указать папку /src/main/resources/META-INF которую мы будем копировать в архив, с помощью тэгов fileSets:

<assembly>
<id>with-dependencies</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${basedir}/src/main/resources/META-INF</directory>
<outputDirectory>META-INF</outputDirectory>
</fileSet>
</fileSets>

<dependencySets>
<dependencySet>
<outputDirectory/>
<outputFileNameMapping/>
<unpack>true</unpack>
</dependencySet>
</dependencySets>
</assembly>

Таким образом наши файлы попадут в архив первыми и уже не будут перезаписаны другими вариантами из библиотек Spring.

Кстати, версию maven-assembly-plugin-а следует использовать 2.2-beta-3 и старше. Предыдующая версия будет копировать все одинаковые файлы в ваш jar, и в одной папке будет несколько файлов с одним именем.

суббота, 7 февраля 2009 г.

Инструмент для просмотра логов

В повседневной работе довольно часто приходится смотреть логи от log4j. Прежде пользовался лишь обычным view-ром, пока не нашел довольно полезный open-source инструмент MindTree Insight, предназначенный именно для этой задачи.

Из настроек отмечу лишь, то что поле Preferences -> Maintain Preferences -> Primary Pattern должно содержать ту же строку форматирования, которую вы указали в настройках log4j.

понедельник, 2 февраля 2009 г.

Проблема с SecurityContext-ом при invalidate http-сессии

В Spring Security есть возможность определения допустимого числа параллельных http-сессий от одного пользователя. Как только кол-во свободных сессий исчерпает себя, то по-умолчанию самая первая из них будет уничтожена через метод invalidate() и ее место займет новая. За настройку такой возможности отвечает тэг concurrent-session-control и его параметр max-sessions.

Проблема, которая была обнаружена мной буквально на днях, следующая - при уничтожении сессии, вместо объекта org.springframework.security.context.SecurityContext приходит null. Выявить эту ошибку может следующий код:

public void onApplicationEvent(ApplicationEvent event) {
if (!(event instanceof HttpSessionDestroyedEvent)) {
return;
}

SecurityContext context = SecurityContextHolder.getContext();
Object principal = context.getAuthentication().getPrincipal();

...

}

Если посмотреть порядок следования фильтров в Spring Security:

  1. CHANNEL_FILTER

  2. CONCURRENT_SESSION_FILTER

  3. SESSION_CONTEXT_INTEGRATION_FILTER

  4. LOGOUT_FILTER

  5. ...

то мы увидим, что фильтр SESSION_CONTEXT_INTEGRATION_FILTER, который как раз и выставляет нужный нам объект SecurityContext, находится после CONCURRENT_SESSION_FILTER фильтра, управляющего параллельными сессиями. Такая "неверная", в данном случае, конфигурация фильтров и приводит к потере SecurityContext-а. Потому завел на это дело багу - SEC-1092. Решения я увидел два - поменять фильтры местами, с помощью конфигурации это делается довольно просто, и выставить объект SecurityContext самому. Второй вариант мне показался более верным. Менять фильтры местами, думаю не стоит, возможно есть причина по которой они расположены именно так.

Нужный нам экземпляр SecurityContext-а можно взять из текущий http-сессии, к которой он и привязан. Хранится он в сессии по имени HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY. Объект класса HttpSessionContextIntegrationFilter как раз и находится на позиции SESSION_CONTEXT_INTEGRATION_FILTER, и выставляет текущий SecurityContext в SecurityContextHolder. Сам код решения взят из этого фильтра и выглядит так:

public void onApplicationEvent(ApplicationEvent event) {
if (!(event instanceof HttpSessionDestroyedEvent)) {
return;
}

HttpSession session = event.getSession();
SecurityContext context = (SecurityContext) session.getAttribute(HttpSessionContextIntegrationFilter.SPRING_SECURITY_CONTEXT_KEY);
SecurityContextHolder.setContext(context);


SecurityContext context = SecurityContextHolder.getContext();
Object principal = context.getAuthentication().getPrincipal();

...

}

вторник, 20 января 2009 г.

Генерация flex-проекта с помощью Maven

До недавнего времени, создавать flex-проекты мне приходилось вручную. Откровенно говоря этот процесс меня не очень радовал, в результате поисков был найден maven-плагин Maven Flex Plugin.

Этот плагин помимо типичных задач компиляции swc и swf-файлов, которые способны выполнять и ряд других плагинов (например Flex-mojos), также позволяет выполнять очень ценную задачу flex:eclipse - подобие eclipse:eclipse, привычного, думаю для всех, способа геренации java-проектов под Eclipse. Для его настройки, в pom-файле необходимо зарегистрировать плагин-репозитарий:

    <pluginRepositories>
<pluginRepository>
<id>flex-plugin-repo</id>
<name>Flex Plugin</name>
<url>http://maven.servebox.org/repository/</url>
<layout>default</layout>
<snapshots>
<enabled>false</enabled>
</snapshots>
<releases>
<updatePolicy>never</updatePolicy>
</releases>
</pluginRepository>
</pluginRepositories>

указать сам плагин:

    <plugin>
<groupId>org.servebox.flex</groupId>
<artifactId>flex-plugin</artifactId>
<extensions>true</extensions>
<inherited>true</inherited>
</plugin>

Теперь можно выполнять: mvn flex:eclipse

Не забудьте определить переменную M2_REPO указывающую на ваш репозитарий во Flex Builder-е, на вкладке Window > Preferences > General > Workspace > Linked Resources

Плагин также регистрирует в создаваемых пректах зависимости на другие swc-библиотеки. Таким образом его достаточно запустить на уровне головного pom-файла, чтобы сгенерировать все дочерние проекты. Хочу отметить, что планиг отлично уживается в одном проекте с Flex-mojos.

четверг, 1 января 2009 г.

С Новым Годом!!!

Дорогие читатели, поздравляю Вас с Новым Годом!!! Хочу пожелать чтобы все мечты, загаданные под бой курантов, обязательно исполнились. Пусть рядом с Вами всегда будут хорошие люди, пусть все трудности и неприятности пройдут мимо Вас. И конечно же желаю Вам просто огромных успехов в работе.

С Новым... .-)