AspectJ для журналирования
Задача: покрутить AOP на как можно более простом примере.
Для этого возьмем:
1) Файл AspectTest.java с подопытным классом
package ajtest;
class AspectTest {
public void printKrivetko() {
System.out.println("Я криветко :)");
}
public void printMedved() {
System.out.println("Я медвед 8]");
}
public static void main(String[] args) {
AspectTest app = new AspectTest();
app.printKrivetko();
app.printMedved();
}
}
2) Файл PrintLogging.aj с простым аспектом
package ajtest;
// наш простой аспект
public aspect PrintLogging {
// наш простой адвайс
after() returning:
call(void AspectTest.print*()) {
System.out.println("^^^ вызыван какой-то метод print*");
}
}
3) Файл build.xml с несложным проектом
<project name="ajtest" default="ajx" basedir=".">
<!-- определяем директории для исходников и скомпилированных классов -->
<property name="src" location="src"/>
<property name="build" location="build"/>
<!-- определяем таск iajc -->
<taskdef
resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">
<classpath>
<pathelement location="lib/aspectjtools.jar"/>
</classpath>
</taskdef>
<!-- цель для компиляции основных классов -->
<target name="compile">
<javac srcdir="${src}" destdir="${build}"/>
</target>
<!-- цель для добавления AOP-функциональности -->
<target name="ajx" depends="compile">
<iajc sourceroots="${src}"
destdir="${build}"
classpath="lib/aspectjrt.jar"
/>
</target>
</project>
4) Библиотеки aspectjrt.jar и aspectjtools.jar из последнего стабильного билда.
Все это разложим по директориям:
+ build + src |- + ajtest |- AspectTest.java |- PrintLogging.aj + lib |- aspectjrt.jar |- aspectjtools.jar build.xml
И соберем ant-ом:
ant
Запускаем (сообщения, создаваемые внутри аспекта, отправляются в стандартный вывод):
java -cp build;lib/aspectjrt.jar ajtest.AspectTest
В консоли должно появиться следующее:
Я криветко :)
^^^ вызыван какой-то метод print*
Я медвед 8]
^^^ вызыван какой-то метод print*
It’s working.
Издевательство с url-pattern
Привязка сервлета к URL только двумя убогими способами («строка начинается с …» и «строка заканчивается на…») — это по нынешним временам совершеннейшая дичь. Благо, неравнодушные люди озаботились ситуацией и написали rewrite-filter.
Перехожу на Spring Framework
В конторе накопилось много ценной информации, которая пока лежит кучами и не приносит отдачи. Перенос ее в базу данных и разработка бизнес-логики — вопрос времени. Рано или поздно придется это делать.
Но уже ясно, что легковесными казуальными решениями не обойтись: во-первых, еще никто не может четко представить задачи и сформулировать детальные требования к проектам, во-вторых, как следствие, настанет момент, когда все эти разнородные костыли будет сложно собрать в систему. Отсюда явно следует, что энтерпрайзу нужно предложить энтерпрайзовое решение. В нашем случае, реализацию бизнес-логики в виде бинов внутри Java EE-контейнера.
Чрезмерную сложность Java EE можно отчасти нивелировать внедрением фреймворка, который позволяет программировать бины, не захламляя код формальными конструкциями. На эту роль идеально подходит Spring Framework.
Кроме того, Spring Framework обещает элегантное решение проблем сквозной функциональности (например, журналирования), готовое MVC для веб-компонентов, аутентификацию / авторизацию и многое другое. Поверхностное чтение документации показало, что в центре внимания находятся как раз-таки насущные проблемы, которые много раз возникали в моих проектах. Это подтверждает (надеюсь) правильность выбора. Остается в который раз запастись терпением и глазными каплями — и в бой.
String.format(…)
Даже в Java (точнее JDK 1.5) можно писать лаконично. Задача: получить незанятый инвентарный номер из БД и вывести его в виде строки из 8 цифр (с ведущими нулями по необходимости):
// открываем транзакцию...
Transaction ta = session.beginTransaction();
// получаем последний занятый номер
Integer lastNumber = (Integer)session.createSQLQuery
("SELECT max(inumber) FROM assets")
.list().get(0);
// ...закрываем транзакцию
ta.commit();
// вот оно! форматируем а ля sprintf
out.print(String.format("%08d", ++lastNumber));
Модуль аутентификации к Jetty
Для написания своего Login Module документация по JAAS в Jetty отправляет к соответствующим исходникам и намекает, что нужно всего лишь накодировать метод getUserInfo. Это чистая правда, но пока врубишься что к чему, Jetty наплюет не один килобайт логов с ошибками. Итак, что получилось:
package levi.ainv;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.mortbay.jetty.plus.jaas.spi.AbstractLoginModule;
import org.mortbay.jetty.plus.jaas.spi.UserInfo;
import org.mortbay.jetty.security.Credential;
public class DBLoginModule extends AbstractLoginModule {
@Override
public UserInfo getUserInfo(String userName) throws Exception {
// в моем случае используется Singleton-объект Engine,
// который хранит хэшмап с юзерами
User user = Engine.itself().selectUser(userName);
// на нет и суда нет
if(user == null) {
return null;
}
// заливаем роли в список
List<String> roles = new ArrayList<String>();
Iterator<Role> userRoles = user.getRoles().iterator();
while(userRoles.hasNext()) {
roles.add(userRoles.next().getName());
}
// ключевой момент - возвращаем имя юзера,
// креденшл, изготовленный из простого строкового пароля, и список с ролями --
// дальше родительский класс сам сравнит пароли и роли,
// которые мы предоставили для заданного имени юзера и решит
// пущать или не пущать
return new UserInfo(userName, Credential.getCredential(user.getPassword()), roles);
}
}
То есть, нужно из своего супер-секретного-хранилища юзерских аккаунтов, где бы оно ни было, вытащить аккаунт с заданным именем, запаковать в объект UserInfo и return его. Больше ни о чем думать не требуется.
Groovy
Еще один язык в очереди на изучение. Скриптовая java — это страшная сила.