У нас есть веб-приложение Java (.war), которое можно разместить на нескольких серверах приложений (Tomcat для собственной разработки, Weblogic 10.3.5 для некоторых клиентов, Websphere для других клиентов и Glassfish для других). Что бы это ни стоило, это jdk 1.6.
Мы используем старую версию struts (1.0.2) для сопоставления действий, в нашем файле web.xml определено несколько фильтров, у нас также есть дополнительные файлы конфигурации для некоторых серверов приложений (например, файл weblogic.xml для weblogic с небольшим количеством элементов). Уровень представления — это весь JSP с некоторыми JS.
У нас есть основная точка входа/класс для обработки http-запросов, который расширяет класс Struts ActionServlet.
Вот моя проблема при развертывании и тестировании приложения на сервере weblogic (WLS) 10.3.5, проблема, которую я собираюсь описать, никогда не встречалась ни на одном из других серверов приложений.
Пользователи попробуют выполнить простое начальное действие регистрации, вызвав начальное действие struts (например, «/login»).
[...]
Фильтры применяются, https-сессия инициируется (выполняется проверка request.getSession(false), а затем вызывается request.getSession(), если HttpSession оказывается нулевым, что ожидается в первых фильтрах).
После успешного результата этого первого действия мы затем перенаправляем внутренний запрос, как показано выше, на следующее действие Struts. Это действие также завершается успешно и перенаправляет запрос на следующее действие Struts.
Прежде чем двигаться дальше, важно отметить, что оба этих действия устанавливают важные атрибуты в объекте HttpSession, которые позже используются в бизнес-логике нашего приложения.
Следующее действие отображается на странице .jsp (которая преобразуется в невнутреннее действие ActionForward):
[...]
Непосредственно перед завершением действий /login и /completeLogin я печатаю идентификатор HttpSession, чтобы убедиться, что один и тот же идентификатор сеанса повторно используется от одного вызова к другому.
Проблема заключается в том, что когда мой ActionServlet отправляет третий запрос через метод RequestDispatcher#forward(ServletRequest,ServletResponse), третье действие завершается неудачей, потому что мы ожидаем, что это произойдет. получить некоторые атрибуты из HttpSession (которые ранее были успешно установлены), но их нет, потому что, к удивлению, был сгенерирован новый HttpSession и передан этому третьему действию вместо HttpSession исходного запроса. (Я печатаю идентификатор и вижу, что он отличается от двух предыдущих напечатанных идентификаторов), поскольку мне не удается получить доступ к этим атрибутам, приложение выдает исключение, и пользователь не может использовать приложение, потому что он/она просто не может войти в систему.
Теперь некоторые вещи, которые я пробовал, прежде чем прийти сюда:
- У нас есть класс, реализующий интерфейс HttpSessionListener, который уведомляет нас, когда HttpSession создается или уничтожается. Во всех протестированных мной случаях я всегда вижу уведомление о создании исходного и второго http-сеанса, но никогда не получаю уведомления о уничтожении сеанса. Я предполагаю, что у weblogic где-то должен быть исходный сеанс, и он никогда не уничтожал его, а вместо этого создавал новый.
- Тем не менее, я проверил вызовы метода HttpSession#invalidate() и не вижу никаких вызовов в наших классах Filters, ActionServlet или Action как таковых в потоке, указанном выше.
- Класс RequestDispatcher зависит от контейнера, т. е. реализация поставщика фактически вызывается в время выполнения при получении экземпляра диспетчера. Я предположил, что что-то не так в диспетчере Oracle, поскольку ни у одного из других поставщиков нет проблемы (и выполняется тот же код), поэтому я открыл запрос на обслуживание в Oracle и все еще общаюсь с некоторыми из их инженеров, чтобы найти решение, но мне очень сложно доказать им мою проблему, несмотря на бесчисленные файлы журналов, которые я отправил им с объяснением проблемы.
- Прочитав некоторую документацию Oracle, я понял, что, как и большинство сервлетов, объекты HttpSession тесно связаны с файлами cookie браузера. В нашей конфигурации мы не используем какую-либо форму сохранения сеанса, мы также не создаем никаких дополнительных файлов cookie, а просто сохраняем атрибуты сеанса http. Наш клиент также подтвердил, что в его браузерах включены файлы cookie. Мне также удалось воспроизвести проблему в двух браузерах внутри страны. Затем я начал исследовать файлы cookie, и на данный момент это, кажется, моя основная идея. Я проверил, присутствовали ли какие-либо файлы cookie после первоначальных фильтров. К моему удивлению, я ожидал отсутствия файлов cookie, поскольку, насколько я понимаю, срок действия файла cookie по умолчанию, созданного для сеанса, истекает после завершения сеанса (закрытие браузера/выход из приложения).
Пример кода исправления:
public class LoginAction extends GenericAction {
private static final Logger log = LoggerFactory.getLogger(LoginAction.class);
private static final String JSESSIONID_COOKIE_NAME = "JSESSIONID";
@Override
public ActionForward internalPerform(ActionMapping mapping, BaseForm form,
HttpServletRequest request, HttpServletResponse response) throws InvalidSessionException {
String clientOwner = null;
String registeredHost = null;
/*
* Temporary patch for Weblogic JAS. Will be removed eventually.
*/
verifyJSessionIdCookies(request, response);
try {
[...]
}
Метод исправления:
protected void verifyJSessionIdCookies(HttpServletRequest request, HttpServletResponse response) {
Cookie[] existingCookies = request.getCookies();
HttpSession session = request.getSession(false);
if (null != existingCookies && null != session) {
for (Cookie cookie : existingCookies) {
if (cookie.getName().equals(JSESSIONID_COOKIE_NAME) && !cookie.getValue().equals(session.getId())) {
log.debug("Updating current client JSESSIONID cookie from {} to value {}", cookie.getValue(),
session.getId());
cookie.setValue(session.getId());
}
}
}
}
}
Наш файл weblogic.xml выглядит следующим образом:
false
org.apache.commons.lang.*
Я также включил журналы HttpDebug на самой консоли администратора weblogic и часто вижу это сообщение, когда пересылка запроса вот-вот завершится неудачно:
(Создание начального сеанса)
[...]
(неудача)
[..]
Итак, я думаю, мой вопрос: сталкивался ли кто-нибудь с этой проблемой раньше? Суть в том, что мой http-сеанс не тот же после RequestDispatcher#forward() при работе в weblogic 10.3.5? Или, возможно, есть какие-нибудь идеи для временного обходного пути?
Что-нибудь, что я, возможно, забыл?
Мое третье действие вызывает request.getSession(), а не request.getSession(false), поскольку предполагается, что он был создан в этот момент, но получение его атрибутов показывает пустую карту/список.
что такого особенного в weblogic, который может вызвать такое поведение?
Пример ActionServlet:
RequestDispatcher rd = getServletContext().getRequestDispatcher(path);
if (null == rd) {
String errorMessage = internal.getMessage(REQUEST_DISPATCHER, path);
log.debug(errorMessage);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, errorMessage);
return;
}
if (null != request.getAttribute(Constants.INCLUDED_REQUEST)) {
rd.include(request, response);
} else {
try {
//fails after this call
rd.forward(request, response); [..]
Третье действие:
public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws IOException, ServletException {
//displays a new id
log.debug("Http session after forward : {}", request.getSession(false).getId());
String mappingPath = mapping.getPath();
boolean outOfSessionAction = outOfSessionPaths.contains(mappingPath);
Attribute attr = null;
if (!outOfSessionAction) {
attr = request.getSession().getAttribute("attr1");
if (attr == null) {
//we should be retrieving this attribute but we fail because
//new HttpSession in the request object
return processException(request, mapping, new BusinessException(true));
}
}
Мобильная версия