SessionContext всегда является нулем в Session-Bean, вызываемом из сервлета
Моя работа состоит в том, чтобы написать своего рода адаптер веб-службы с использованием сервлета, который затем вызывает адаптер EJB (веб-служба SOAP), который, в свою очередь, вызывает существующие методы службы (также EJB). Существующая архитектура основана на EJB 2.0, который я пока не могу изменить. Предполагается, что внешние клиенты получают доступ к сервлету, а не к классу веб-сервиса напрямую. Причина этого заключается в том, что некоторая предварительная обработка должна быть выполнена с использованием информации в HTTPRequest (выборка и отображение идентификатора пользователя из сертификата или, соответственно, заголовка HTTP). Таким образом, сервлет реагирует на метод doPost(), выполняет предварительную обработку, такую как синтаксический анализ и маршалинг данных SOAP XML, а затем вызывает адаптер EJB (Session Bean без сохранения состояния), точнее методы веб-сервиса, которые затем будут запускать существующие методы службы в другом EJB. Это работает хорошо до тех пор, пока EJB-компоненты не требуют существующей сессии, соответственно. SessionContext, как в следующем случае, когда обрабатывается откат транзакции:
protected void preventTransactionRolledBackException() {
if (this.getSessionContext().getRollbackOnly()) {
this.getSessionContext().setRollbackOnly();
}
}
В моей настройке SessionContext всегда имеет значение null. Поскольку все приложение уже довольно сложное, я пытаюсь опубликовать упрощенную настройку, используя только сервлет и один EJB, надеясь, что я не исключаю что-то важное. Система: WebSphere Application Server 8.5
Servlet:
public class NewZekEclsServiceServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String operation = null;
response.setContentType("text/xml");
try {
PrintWriter out = response.getWriter();
// Get Header data
// Get Attribute data
// Get Body data:
InputStream body = request.getInputStream();
String xml = IOUtils.toString(body, "UTF-8");
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setNamespaceAware(true); // required as several namespaces might be used
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.parse(new InputSource(new StringReader(xml)));
operation = getOperation(doc); // method which scans the xml to retrieve the correct operation to use.
if (operation.equalsIgnoreCase("Ping")) {
// Calling the Adapter Bean:
EclsTestBeanBean eclsTestBean = new EclsTestBeanBean();
eclsTestBean.ejbCreate();
PingIn pingIn = new PingIn();
PingOut pingOut = eclsTestBean.ping(pingIn);
String xmlString = convertEclsObjectToSOAPString(pingOut);
out.print(xmlString);
}
}
}
}
EJB (генерируется Rational Application Developer 9.0):
/**
* Bean implementation class for Session Bean: EclsTestBean
*
* @ejb.bean
* name="EclsTestBean"
* type="Stateless"
* jndi-name="ejb/ch/zek/ecls/EclsTestBeanHome"
* view-type="remote"
* transaction-type="Bean"
*
* @ejb.home
* remote-class="ch.zek.ecls.EclsTestBeanHome"
*
* @ejb.interface
* remote-class="ch.zek.ecls.EclsTestBean"
*
*/
public class EclsTestBeanBean implements javax.ejb.SessionBean {
private Log log = LogFactory.getLog(EcodeAbfragenAction.class);
private SessionContext mySessionCtx;
public SessionContext getSessionContext() {
return mySessionCtx;
}
public void setSessionContext(SessionContext ctx) {
mySessionCtx = ctx;
}
public void ejbCreate() throws CreateException {}
public void ejbActivate() {}
public void ejbPassivate() {}
public void ejbRemove() {}
/**
* Basic ping service for ECLS
* @param parameters
* @return
* @throws PingEntityNotFoundException
* @throws PingPermissionException
* @throws PingSystemException
*/
public ch.zek.ecls.PingOut ping(ch.zek.ecls.PingIn parameters) throws java.rmi.RemoteException, ch.zek.ecls.PingPermissionException, ch.zek.ecls.PingEntityNotFoundException, ch.zek.ecls.PingSystemException {
PingOut pingOut = new PingOut();
String pingAnswer = "Ping_ECLS_v1";
String adapter = "";
String operation = "";
Parameter[] msgParams = new Parameter[1];
String eclsEnvironment = "TEST";
pingAnswer += "_" + eclsEnvironment;
logAvailableEjbs();
try {
if (mySessionCtx != null) { // Why can it be null at all?
log.debug("mySessionCtx: " + mySessionCtx.getContextData());
} else {
log.debug("mySessionCtx was null");
InitialContext ic = new InitialContext();
mySessionCtx = (SessionContext) ic.lookup("java:comp/env/sessionContext"); // gives error: javax.naming.NameNotFoundException: Name "comp/env/sessionContext" not found in context "java:".
System.out.println("mySessionCtx: " + mySessionCtx);
}
} catch (Exception e) {
e.printStackTrace();
}
// creating the SOAP data... not relevant for the problem.
msgParams[0] = new Parameter();
msgParams[0].setValue(pingAnswer);
SystemMessage systemMessage = new SystemMessage();
systemMessage.setCode("OK");
systemMessage.setMessage("Ping");
systemMessage.setParameter(msgParams);
pingOut.setSystemMessage(systemMessage);
return pingOut;
}
/**
* ONLY USED DURING DEVELOPMENT:
* Helper method to print "accessible" EJBs.
*/
protected void logAvailableEjbs() {
try {
//Get the Initial Context for the JNDI lookup for a local EJB
InitialContext ic = new InitialContext();
NamingEnumeration<NameClassPair> list;
String level = "";
String name = "";
list = ic.list(level);
while (list.hasMore()) {
name = list.next().getName();
System.out.println(level + "/" + name);
}
level = "ejb";
list = ic.list(level);
while (list.hasMore()) {
name = list.next().getName();
System.out.println(level + "/" + name);
}
level = "java:comp";
list = ic.list(level);
while (list.hasMore()) {
name = list.next().getName();
System.out.println(level + "/" + name);
}
level = "java:comp/env";
list = ic.list(level);
while (list.hasMore()) {
name = list.next().getName();
System.out.println(level + "/" + name);
}
/*
level = "java:comp/env/ejb"; // Throws Error!
list = ic.list(level);
while (list.hasMore()) {
name = list.next().getName();
System.out.println(level + "/" + name);
}
*/
level = "java:global";
list = ic.list(level);
while (list.hasMore()) {
name = list.next().getName();
System.out.println(level + "/" + name);
}
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Мы тестируем установку с помощью инструмента SoapUI ( http://www.soapui.org/). Опять же, мы получаем правильные результаты для простой службы Ping(), но, как только требуется материал, связанный с сеансом, он завершается неудачно.
Конечно, я провел некоторое исследование, особенно интересной была эта ссылка: Как получить SessionContext в JBOSS, которая также ссылается на: http://javahowto.blogspot.co.uk/2006/06/4-ways-to-get-ejbcontext-in-ejb-3.html
А что касается жизненного цикла EJB: docs.oracle.com/cd/E13224_01/wlw/docs100/guide/ejb/session/conSessionBeanLifeCycle.html Учебное пособие по J2EE: docs.oracle.com/javaee/6/tutorial/doc/gipss.html#gipsx (извините, я не могу опубликовать более 2 ссылок...)
Но, как отмечено в коде, я не могу получить доступ к java:comp/env/ejb/sessionContext. Ну... даже не Java:comp/env/ejb. Я написал грязный вспомогательный метод, чтобы увидеть, что на самом деле доступно для EJB, и получить следующий вывод:
SystemOut O /jta
SystemOut O /eis
SystemOut O /cell
SystemOut O /thisNode
SystemOut O /DefaultDatasource
SystemOut O /services
SystemOut O /jdbc
SystemOut O /servername
SystemOut O /com.ibm.websphere.ejbcontainer
SystemOut O /com
SystemOut O /zek
SystemOut O /wm
SystemOut O /ejb
SystemOut O /Increment
SystemOut O /tm
SystemOut O ejb/ivtEJBObject
SystemOut O ejb/ch
SystemOut O ejb/mgmt
SystemOut O java:comp/ValidatorFactory
SystemOut O java:comp/TransactionSynchronizationRegistry
SystemOut O java:comp/ORB
SystemOut O java:comp/Validator
SystemOut O java:comp/UserTransaction
SystemOut O java:comp/env
SystemOut O java:comp/BeanManager
SystemOut O java:comp/websphere
SystemOut O java:comp/HandleDelegate
SystemOut O java:global/com.ibm.ws.AppNameSpaces
SystemOut O java:global/NewZekEar
SystemOut O java:global/SchedulerCalendars
SystemOut O java:global/DefaultApplication
SystemOut O java:global/cell
SystemOut O java:global/query
SystemOut O java:global/ManagementEJB
SystemOut O java:global/ivtApp
Я также попытался использовать аннотацию:
@Resource
private SessionContext mySessionCtx;
или установить тип транзакции по-другому:
transaction-type="Container"
Ничего не помогло Я понял, что SessionContext должен быть создан автоматически. Но если нет - какой вариант нужно создать новый? Следующий вопрос: сервлет также "создает" сеанс, к которому я могу получить доступ (request.getSession()), но это нечто иное (HTTPSession). Я думаю, я не могу поделиться или "преобразовать" этот объект сеанса в сессионный компонент?
1 ответ
Вы создали EclsTestBeanBean
сам:
EclsTestBeanBean eclsTestBean = new EclsTestBeanBean();
EJB полагается на обертывание объекта в прокси, чтобы позволить контейнеру добавлять дополнительные сервисы, включая зависимости, внедряющие контекст сеанса, что означает, что только контейнер может их создавать и выдавать. Вы также обнаружите, что этот объект не будет выполнять никаких сервисов EJB, это просто POJO здесь.
Чтобы запросить EJB из контейнера, вам нужно либо запросить его через внедрение зависимостей, либо найти его из JNDI, как показано в следующем ответе:
Менеджер сущностей JPA введен неправильно - Weblogic
Тогда следующая проблема будет в том, что ваш bean-компонент не доступен в JNDI - я думаю, это потому, что вы не определили для него локальный или удаленный интерфейс. Это хорошо в EJB3, где используется представление "без интерфейса", но не в EJB2: http://help.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.jst.ejb.doc.user%2Ftopics%2Fcearch.html
Так что я бы предложил - напишите себе интерфейс, (надеюсь) тогда EclsTestBeanBean
затем становится доступным в JNDI и ищет его оттуда, а не создает его экземпляр.
(С аннотацией @Resource помните, что вся работа на основе аннотаций была также добавлена в EJB3.)