Когда мы поставили перед собой цель, чтобы Java EE 6 сертифицировала Apache Tomcat как Apache TomEE , вот некоторые из пробелов, которые нам пришлось заполнить, чтобы наконец передать Java EE 6 TCK.
Не полный список, но некоторые основные моменты, которые могут быть неочевидны даже при существующих ответах.
Нет TransactionManager
Управление транзакциями определенно требуется для любого сертифицированного сервера. В любом веб-компоненте (сервлет, фильтр, слушатель, управляемый bean-компонент jsf) вы можете получить UserTransaction
, например:
@Resource UserTransaction transaction;
Вы можете использовать javax.transaction.UserTransaction
для создания транзакций. Все ресурсы, к которым вы прикасаетесь в рамках этой транзакции, должны быть зарегистрированы в этой транзакции. Это включает, но не ограничивается следующими объектами:
javax.sql.DataSource
javax.persistence.EntityManager
javax.jms.ConnectionFactory
javax.jms.QueueConnectionFactory
javax.jms.TopicConnectionFactory
javax.ejb.TimerService
Например, если в сервлете вы запускаете транзакцию, то:
- Обновление базы данных
- Запустить JMS-сообщение в теме или очереди
- Создать таймер для выполнения работы в более поздний момент
.. и затем одна из этих вещей не сработает, или вы просто решите позвонить rollback()
на UserTransaction
, тогда все эти вещи будут отменены.
Нет пула подключений
Чтобы было очень ясно, есть два вида пула соединений:
- Транзакционная поддержка пула соединений
- Нетранзакционный пул подключений
Спецификации Java EE строго не требуют пулов соединений, однако, если у вас есть пул соединений, он должен учитывать транзакции, иначе вы потеряете управление транзакциями.
Что это означает в основном:
- У всех в одной транзакции должно быть одинаковое соединение из пула
- Соединение не следует возвращать в пул, пока транзакция не завершится (принятие или откат), независимо от того, кто-то вызвал
close()
или любой другой метод в DataSource
.
Общая библиотека, используемая в Tomcat для пула соединений, - commons-dbcp. Мы хотели также использовать это в TomEE, однако он не поддерживал пул соединений с поддержкой транзакций, поэтому мы фактически добавили эту функциональность в commons-dbcp (yay, Apache), и он существует с версии commons-dbc 1.4.
Обратите внимание, что добавления commons-dbcp в Tomcat все еще недостаточно для получения пула транзакционных соединений. Вам все еще нужен менеджер транзакций, и вам все еще нужен контейнер для выполнения регистрации соединений с объектами TransactionManager
через Synchronization
.
В Java EE 7 говорится о добавлении стандартного способа шифрования паролей БД и их упаковки с приложением в защищенный файл или внешнее хранилище. Это будет еще одна функция, которую Tomcat не будет поддерживать.
Нет интеграции безопасности
Безопасность WebServices, JAX-RS SecurityContext, защита EJB, вход в систему JAAS и JAAC - все это концепции безопасности, которые по умолчанию не «подключаются» в Tomcat, даже если вы по отдельности добавляете библиотеки, такие как CXF, OpenEJB и т. Д.
Все эти API, конечно, предполагают совместную работу на сервере Java EE. Нам пришлось проделать довольно много работы, чтобы заставить все это взаимодействовать и сделать это поверх API Tomcat Realm
, чтобы люди могли использовать все существующие реализации Tomcat Realm
для управления своей "Java EE" безопасность. Это действительно безопасность Tomcat, просто она очень хорошо интегрирована.
JPA Integration
Да, вы можете поместить провайдера JPA в файл .war и использовать его без помощи Tomcat. При таком подходе вы не получите:
@PersistenceUnit EntityManagerFactory
впрыск / поиск
@PersistenceContext EntityManager
впрыск / поиск
-
EntityManager
подключен к транзакционному пулу соединений
- JTA-Managed
EntityManager
поддержка
- Расширенные контексты персистентности
JTA-Managed EntityManager
в основном означает, что два объекта в одной и той же транзакции, которые хотят использовать EntityManager
, увидят оба одинаковых EntityManager
, и нет необходимости явно передавать EntityManager
. Все это «прохождение» сделано для вас контейнером.
Как это достигается? Просто, EntityManager
, который вы получили из контейнера, является подделкой. Это обертка. Когда вы используете его, он ищет в текущей транзакции настоящий EntityManager
и делегирует вызов этому EntityManager
. В этом причина таинственного метода EntityManager.getDelegate()
, поэтому пользователи могут получить real EntityManager, если они хотят, и использовать любые нестандартные API. Делайте это с большой осторожностью, и никогда не держите ссылку на делегата EntityManager
, иначе у вас будет серьезная утечка памяти. Делегат EntityManager
обычно сбрасывается, закрывается, очищается и сбрасывается по завершении транзакции. Если вы все еще держитесь за ссылку, вы предотвратите сборку мусора этого EntityManager
и, возможно, всех данных, которые он содержит.
- Всегда безопасно хранить ссылку на
EntityManager
, который вы получили из контейнера
- Неверно хранить ссылку на
EntityManager.getDelegate()
- Будьте очень осторожны, обращаясь к
EntityManager
, который вы создали сами с помощью EntityManagerFactory
- вы на 100% ответственны за его управление.
Интеграция CDI
Я не хочу чрезмерно упрощать CDI, но я считаю, что он слишком велик, и многие люди не смотрят серьезно - он включен в "когда-нибудь" список для многих людей :) Так что это просто пара основных моментов, о которых, я думаю, «веб-парень» хотел бы знать.
Вы знаете все, что вам нужно делать в обычном веб-приложении? Потянув вещи в и из HttpSession
весь день? Использование String
для ключа и непрерывного приведения объектов, которые вы получаете из HttpSession
. Вы, вероятно, пошлите служебный код, чтобы сделать это для вас.
CDI также имеет этот код утилиты, он называется @SessionScoped
. Любой объект, отмеченный @SessionScoped
, помещается и отслеживается в HttpSession
для вас. Вы просто запрашиваете инъекцию объекта в ваш сервлет через @Inject FooObject
, и контейнер CDI будет отслеживать «реальный» экземпляр FooObject так же, как я описал отслеживание транзакций EntitityManager
. Абракадабра, теперь ты можешь удалить кучу кода:)
Делаете какие-нибудь getAttribute
и setAttribute
на HttpServletRequest
? Ну, вы можете удалить это тоже с помощью @RequestScoped
таким же образом.
И, конечно, есть @ApplicationScoped
для устранения вызовов getAttribute
и setAttribute
, которые вы, возможно, делаете на ServletContext
Чтобы сделать вещи еще круче, любой отслеживаемый объект может реализовать @PostConstruct
, который вызывается при создании компонента, и метод @PreDestroy
, который должен быть уведомлен, когда указанная "область действия" завершена (сеанс завершен, запрос окончен, приложение закрывается).
CDI может сделать намного больше, но этого достаточно, чтобы кто-то захотел переписать старое веб-приложение.
Некоторые разборчивые вещи
В Java EE 6 добавлены некоторые вещи, которые находятся в рулевой рубке Tomcats, но не были добавлены. Они не требуют больших объяснений, но объясняют большую часть «заполнения пробелов».
- Поддержка
@DataSourceDefinition
- Поддержка Global JNDI (
java:global
, java:app
, java:module
)
- Enum инъекция через
@Resource MyEnum myEnum
и
- Инъекция класса через
@Resource Class myPluggableClass
и
- Поддержка
@Resource(lookup="foo")
Незначительные баллы, но может быть невероятно полезно определить DataSource
в приложении портативным способом, обмениваться записями JNDI между веб-приложениями и иметь простую возможность сказать «ищите эту вещь и внедряйте ее»
Заключение * +1201 *
Как уже упоминалось, не полный список. Нет упоминаний об EJB, JMS, JAX-RS, JAX-WS, JSF, валидации бинов и других полезных вещах. Но, по крайней мере, некоторые идеи о вещах часто упускаются из виду, когда люди говорят о том, что такое Tomcat, а что нет. Также помните, что то, что вы, возможно, считаете «Java EE», может не соответствовать фактическому определению. Благодаря веб-профилю Java EE сократилась. Это было преднамеренно в отношении "Java EE слишком тяжел, и мне все это не нужно".
Если вы удалите EJB из веб-профиля, вот что у вас осталось:
Java-сервлеты
Java ServerPages (JSP)
Java ServerFaces (JSF)
API транзакций Java (JTA)
API персистентности Java (JPA)
Контексты Java и внедрение зависимостей (CDI)
Проверка бобов
Это чертовски полезный стек.