Glassfish Tyrus ServerEndpoint instantiation failure - how can I diagnose the error? - java

I have a ServerEndpoint class that throws this error as soon as I attempt to connect to it from the client. I presume that some sort of dependency is missing, i.e a bundled library that is missing another jar, although it may be some other problem.
I see the following stack trace in the server log, with no additional information.
An exception or error occurred in the container during the request processing
java.lang.NoClassDefFoundError: Could not initialize class org.example.ServerSocketEndpoint
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at java.lang.Class.newInstance(Class.java:374)
at org.glassfish.tyrus.core.ReflectionHelper.getInstance(ReflectionHelper.java:807)
at org.glassfish.tyrus.core.DefaultComponentProvider.create(DefaultComponentProvider.java:60)
at org.glassfish.tyrus.core.ComponentProviderService.getInstance(ComponentProviderService.java:232)
at org.glassfish.tyrus.core.ComponentProviderService.getEndpointInstance(ComponentProviderService.java:293)
at org.glassfish.tyrus.server.TyrusServerEndpointConfigurator.getEndpointInstance(TyrusServerEndpointConfigurator.java:160)
at org.glassfish.tyrus.core.AnnotatedEndpoint$1.getEndpointInstance(AnnotatedEndpoint.java:144)
at org.glassfish.tyrus.core.ComponentProviderService.getInstance(ComponentProviderService.java:149)
at org.glassfish.tyrus.core.AnnotatedEndpoint.callMethod(AnnotatedEndpoint.java:452)
at org.glassfish.tyrus.core.AnnotatedEndpoint.onError(AnnotatedEndpoint.java:507)
at org.glassfish.tyrus.core.TyrusEndpointWrapper.onConnect(TyrusEndpointWrapper.java:657)
at org.glassfish.tyrus.core.TyrusWebSocket.onConnect(TyrusWebSocket.java:141)
at org.glassfish.tyrus.core.TyrusWebSocketEngine$TyrusConnection.<init>(TyrusWebSocketEngine.java:611)
at org.glassfish.tyrus.core.TyrusWebSocketEngine$SuccessfulUpgradeInfo.createConnection(TyrusWebSocketEngine.java:556)
at org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler.init(TyrusHttpUpgradeHandler.java:111)
at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:777)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:673)
at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:99)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:174)
at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:357)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:260)
at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:188)
at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:191)
at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:168)
at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:189)
at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:119)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:288)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:206)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:136)
at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:114)
at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:77)
at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:838)
at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:113)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:115)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:55)
at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:135)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:564)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:544)
at java.lang.Thread.run(Thread.java:745)
]]
How can I get more relevant information? I am running the server with --debug.
I also added the following to my domain configuration in order to monitor class loading:
<jvm-options>-verbose:class</jvm-options>
<jvm-options>-XX:LogFile=${com.sun.aas.instanceRoot}/logs/jvm.log</jvm-options>
<jvm-options>-XX:+LogVMOutput</jvm-options>
Is it possible to increase the verbosity further?
I am running GlassFish Server Open Source Edition 4.0 (build 89) on Linux x64 with the latest Oracle Java 7 JDK & Tyrus 1.7
Any ideas would be welcome.
The endpoint stripped to its core; I tried to leave anything that could cause issues:
#ServerEndpoint(
value = "/main",
encoders = { BinaryEncoder.class }, // TextEncoder.class
decoders = { BinaryDecoder.class } // TextDecoder.class
)
#Stateful
public class ServerSocketEndpoint implements IServerSocketEndpoint
{
static {
logger = Debug.getLogger(ServerSocketEndpoint.class);
ds = DB.initDs();
gson = GsonPlus.create();
sessions = Collections.synchronizedSet(new HashSet<Session>());
peers = Collections.synchronizedSet(new HashSet<ISocketPeer>());
}
protected static final DataSource ds;
protected static final Logger logger;
protected static final Gson gson;
protected static final Set<Session> sessions;
protected static final Set<ISocketPeer> peers;
protected TyrusSession _session; // TyrusSession implements JSR-356 Session
protected long _socketSessionId;
protected BasicPeer _peer;
public static void send(final Session session, Msg msg)
{
RemoteEndpoint.Async remote = session.getAsyncRemote();
if (session.isOpen())
{
try {
remote.sendObject(msg);
logger.log(Level.INFO, String.format("send() msg: %s", msg.toJson(gson)));
}
catch (IllegalArgumentException ex) {
Logger.getLogger(ServerSocketEndpoint.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public ServerSocketEndpoint()
{
}
#Override
public void close(CloseReason closeReason)
{
if (_session == null)
return;
try {
_peer.free();
_session.close(closeReason);
_session = null;
}
catch (IOException ex) {
methodException(ex);
}
}
#OnOpen
public void onOpen(Session session, EndpointConfig config)
{
_session = (TyrusSession)session;
if (ds == null) {
// No connection available somehow - nothing to do except log the error
}
InetAddress clientExtIp;
try {
clientExtIp = InetAddress.getByName(_session.getRemoteAddr());
}
catch (UnknownHostException ex) {
}
try (Connection dbc = ds.getConnection())
{
CallableStatement proc = dbc.prepareCall("{call newSocketSession(?, ?, ?, ?, ?, ?)}");
proc.setBytes("extIp", clientExtIp.getAddress());
proc.setString("extHostName", clientExtIp.getHostName());
proc.registerOutParameter("id", Types.BIGINT);
if (proc.execute()) {
// Process results
}
// Stash a reference to this DB socket session
_socketSessionId = proc.getLong("id");
}
catch (SQLException ex) {
methodException(ex);
}
}
#OnError
public void onError(Session session, Throwable ex)
{
methodException(ex);
}
#OnMessage
public void onMessage(final Session session, Msg msg) throws IOException, EncodeException
{
// deal with messages
}
#OnClose
public void onClose(Session session)
{
sessions.remove(session);
_session = null;
_peer = null;
}
}
Okay so I got it to work with Pavel's help.
Tyrus should not be included in the WAR as this results in duplicate files on the class path.
The apache.commons.codec.binary.Base64 class was not being linked in (required for GsonPlus).
So my question becomes the following more specifically: is there a non-brute-force method of diagnosing such CNFE errors, i.e without de-constructing & re-constructing the endpoint piece by piece?

Related

failed to create a child event loop/failed to open a new selector/Too many open files

I am getting errors like "failed to create a child event loop/failed to open a new selector/Too many open files" when there are 30 or more concurrent requests...How to solve the above errors? Am I doing anything wrong? I am using Spring boot and Java cassandra driver. Below is the connection file:
public class Connection {
public static Session getConnection() {
final Cluster cluster = Cluster.builder().addContactPoint(ConnectionBean.getCASSANDRA_DB_IP())
.withQueryOptions(new QueryOptions().setConsistencyLevel(ConsistencyLevel.LOCAL_ONE))
.withCredentials(ConnectionBean.getCASSANDRA_USER(), ConnectionBean.getCASSANDRA_PASSWORD())
.withPoolingOptions(poolingOptions)
.build();
final Session session = cluster.connect(ConnectionBean.getCASSANDRA_DB_NAME());
return session;
}
}
Below is the ConnectionBean file which I used in Connection file:
public class ConnectionBean {
public static String CASSANDRA_DB_IP;
public static String CASSANDRA_DB_NAME;
public static String CASSANDRA_USER;
public static String CASSANDRA_PASSWORD;
public ConnectionBean() {
}
public ConnectionBean(String CASSANDRA_DB_IP,String CASSANDRA_DB_NAME,String CASSANDRA_USER,String CASSANDRA_PASSWORD) {
this.CASSANDRA_DB_IP=CASSANDRA_DB_IP;
this.CASSANDRA_DB_NAME=CASSANDRA_DB_NAME;
this.CASSANDRA_USER=CASSANDRA_USER;
this.CASSANDRA_PASSWORD=CASSANDRA_PASSWORD;
}
public static String getCASSANDRA_DB_IP() {
return CASSANDRA_DB_IP;
}
public static void setCASSANDRA_DB_IP(String cASSANDRA_DB_IP) {
CASSANDRA_DB_IP = cASSANDRA_DB_IP;
}
public static String getCASSANDRA_DB_NAME() {
return CASSANDRA_DB_NAME;
}
public static void setCASSANDRA_DB_NAME(String cASSANDRA_DB_NAME) {
CASSANDRA_DB_NAME = cASSANDRA_DB_NAME;
}
public static String getCASSANDRA_USER() {
return CASSANDRA_USER;
}
public static void setCASSANDRA_USER(String cASSANDRA_USER) {
CASSANDRA_USER = cASSANDRA_USER;
}
public static String getCASSANDRA_PASSWORD() {
return CASSANDRA_PASSWORD;
}
public static void setCASSANDRA_PASSWORD(String cASSANDRA_PASSWORD) {
CASSANDRA_PASSWORD = cASSANDRA_PASSWORD;
}
}
Below is the class from where ConnectionBean variables are initialized :
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
private static final String LOGIN_PROCESSING_URL = "/login";
private static final String LOGIN_FAILURE_URL = "/login?error";
private static final String LOGIN_URL = "/login";
#Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
#Autowired
private DataSource dataSource;
#Value("${spring.queries.users-query}")
private String usersQuery;
#Value("${spring.queries.roles-query}")
private String rolesQuery;
#Value("${CASSANDRA_DB_IP}")
public String CASSANDRA_DB_IP;
#Value("${CASSANDRA_DB_NAME}")
public String CASSANDRA_DB_NAME;
#Value("${CASSANDRA_USER}")
public String CASSANDRA_USER;
#Value("${CASSANDRA_PASSWORD}")
public String CASSANDRA_PASSWORD;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
ConnectionBean cb = new ConnectionBean(CASSANDRA_DB_IP, CASSANDRA_DB_NAME, CASSANDRA_USER, CASSANDRA_PASSWORD);
auth.jdbcAuthentication().usersByUsernameQuery(usersQuery).authoritiesByUsernameQuery(rolesQuery)
.dataSource(dataSource).passwordEncoder(bCryptPasswordEncoder);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
// Not using Spring CSRF here to be able to use plain HTML for the login page
http.csrf().disable()
// Register our CustomRequestCache, that saves unauthorized access attempts, so
// the user is redirected after login.
.requestCache().requestCache(new CustomRequestCache())
// Restrict access to our application.
.and().authorizeRequests()
// Allow all flow internal requests.
.requestMatchers(SecurityUtils::isFrameworkInternalRequest).permitAll()
// Allow all requests by logged in users.
.anyRequest().authenticated()
// Configure the login page.
.and().formLogin().loginPage(LOGIN_URL).permitAll().loginProcessingUrl(LOGIN_PROCESSING_URL)
.failureUrl(LOGIN_FAILURE_URL)
// Register the success handler that redirects users to the page they last tried
// to access
.successHandler(new SavedRequestAwareAuthenticationSuccessHandler())
// Configure logout
.and().logout().logoutSuccessUrl(LOGOUT_SUCCESS_URL);
}
/**
* Allows access to static resources, bypassing Spring security.
*/
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
// Vaadin Flow static resources
"/VAADIN/**",
// the standard favicon URI
"/favicon.ico",
// web application manifest
"/manifest.json", "/sw.js", "/offline-page.html",
// icons and images
"/icons/**", "/images/**",
// (development mode) static resources
"/frontend/**",
// (development mode) webjars
"/webjars/**",
// (development mode) H2 debugging console
"/h2-console/**",
// (production mode) static resources
"/frontend-es5/**", "/frontend-es6/**");
}
}
And finally, below is the class through which I am querying cassandra data:
public class getData {
Session session;
public getData(){
session = Connection.getConnection();
getDataTable();
}
private void getDataTable() {
try {
String query = "SELECT * FROM tableName";
ResultSet rs = session.execute(query);
for (Row row : rs) {
/*Do some stuff here using row*/
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
If getConnection() is being invoked for every request, you are creating a new Cluster instance each time.
This is discouraged because one connection is created between your client and a C* node for each Cluster instance, and for each Session a connection pool of at least one connection is created for each C* node.
If you are not closing your Cluster instances after a request completes, these connections will remain open. After a number of requests, you'll have so many connections open that you will run out of file descriptors in your OS.
To resolve this issue, create only one Cluster and Session instance and reuse it between requests. This strategy is outlined in 4 simple rules when using the DataStax drivers for Cassandra:
Use one Cluster instance per (physical) cluster (per application lifetime)
Use at most one Session per keyspace, or use a single Session and explicitely specify the keyspace in your queries

Access the session variable within a ServletContextListener

I am implementing an upload feature using Grails where basically a user gets to upload a text file and then the system will persist each line of that text file as a database record. While the uploading works fine, larger files take time to process and therefore they ask to have a progress bar so that users can determine if their upload is still processing or an actual error has occurred.
To do this, what I did is to create two URLs:
/upload which is the actual URL that receives the uploaded text file.
/upload/status?uploadToken= which returns the status of a certain upload based on its uploadToken.
What I did is after processing each line, the service will update a session-level counter variable:
// import ...
class UploadService {
Map upload(CommonsMultipartFile record, GrailsParameterMap params) {
Map response = [success: true]
try {
File file = new File(record.getOriginalFilename())
FileUtils.writeByteArrayToFile(file, record.getBytes())
HttpSession session = WebUtils.retrieveGrailsWebRequest().session
List<String> lines = FileUtils.readLines(file, "UTF-8"), errors = []
String uploadToken = params.uploadToken
session.status.put(uploadToken,
[message: "Checking content of the file of errors.",
size: lines.size(),
done: 0])
lines.eachWithIndex { l, li ->
// ... regex checking per line and appending any error to the errors List
session.status.get(uploadToken).done++
}
if(errors.size() == 0) {
session.status.put(uploadToken,
[message: "Persisting record to the database.",
size: lines.size(),
done: 0])
lines.eachWithIndex { l, li ->
// ... Performs GORM manipulation here
session.status.get(uploadToken).done++
}
}
else {
response.success = false
}
}
catch(Exception e) {
response.success = false
}
response << [errors: errors]
return response
}
}
Then create a simple WebSocket implementation that connects to the /upload/status?uploadToken= URL. The problem is that I cannot access the session variable on POGOs. I even change that POGO into a Grails service because I thought that is the cause of the issue, but I still can't access the session variable.
// import ...
#ServerEndpoint("/upload/status")
#WebListener
class UploadEndpointService implements ServletContextListener {
#OnOpen
public void onOpen(Session userSession) { /* ... */ }
#OnClose
public void onClose(Session userSession, CloseReason closeReason) { /* ... */ }
#OnError
public void onError(Throwable t) { /* ... */ }
#OnMessage
public void onMessage(String token, Session userSession) {
// Both of these cause IllegalStateException
def session = WebUtils.retrieveGrailsWebRequest().session
def session = RequestContextHolder.currentRequestAttributes().getSession()
// This returns the session id but I don't know what to do with that information.
String sessionId = userSession.getHttpSessionId()
// Sends the upload status through this line
sendMessage((session.get(token) as JSON).toString(), userSession)
}
private void sendMessage(String message, Session userSession = null) {
Iterator<Session> iterator = users.iterator()
while(iterator.hasNext()) {
iterator.next().basicRemote.sendText(message)
}
}
}
And instead, gives me an error:
Caused by IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case,
use RequestContextListener or RequestContextFilter to expose the current request.
I already verified that the web socket is working by making it send a static String content. But what I want is to be able to get that counter and set it as the send message. I'm using Grails 2.4.4 and the Grails Spring Websocket plugin, while looks promising, is only available from Grails 3 onwards. Is there any way to achieve this, or if not, what approach should I use?
Much thanks to the answer to this question that helped me greatly solving my problem.
I just modified my UploadEndpointService the same as the one on that answer and instead of making it as a service class, I reverted it back into a POGO. I also configured it's #Serverendpoint annotation and added a configurator value. I also added a second parameter to the onOpen() method. Here is the edited class:
import grails.converters.JSON
import grails.util.Environment
import javax.servlet.annotation.WebListener
import javax.servlet.http.HttpSession
import javax.servlet.ServletContext
import javax.servlet.ServletContextEvent
import javax.servlet.ServletContextListener
import javax.websocket.CloseReason
import javax.websocket.EndpointConfig
import javax.websocket.OnClose
import javax.websocket.OnError
import javax.websocket.OnMessage
import javax.websocket.OnOpen
import javax.websocket.server.ServerContainer
import javax.websocket.server.ServerEndpoint
import javax.websocket.Session
import org.apache.log4j.Logger
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.codehaus.groovy.grails.web.json.JSONObject
import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes
import org.springframework.context.ApplicationContext
#ServerEndpoint(value="/ep/maintenance/attendance-monitoring/upload/status", configurator=GetHttpSessionConfigurator.class)
#WebListener
class UploadEndpoint implements ServletContextListener {
private static final Logger log = Logger.getLogger(UploadEndpoint.class)
private Session wsSession
private HttpSession httpSession
#Override
void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.servletContext
ServerContainer serverContainer = servletContext.getAttribute("javax.websocket.server.ServerContainer")
try {
if (Environment.current == Environment.DEVELOPMENT) {
serverContainer.addEndpoint(UploadEndpoint)
}
ApplicationContext ctx = (ApplicationContext) servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
GrailsApplication grailsApplication = ctx.grailsApplication
serverContainer.defaultMaxSessionIdleTimeout = grailsApplication.config.servlet.defaultMaxSessionIdleTimeout ?: 0
} catch (IOException e) {
log.error(e.message, e)
}
}
#Override
void contextDestroyed(ServletContextEvent servletContextEvent) {
}
#OnOpen
public void onOpen(Session userSession, EndpointConfig config) {
this.wsSession = userSession
this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName())
}
#OnMessage
public void onMessage(String message, Session userSession) {
try {
Map params = new JSONObject(message)
if(httpSession.status == null) {
params = [message: "Initializing file upload.",
size: 0,
token: 0]
sendMessage((params as JSON).toString())
}
else {
sendMessage((httpSession.status.get(params.token) as JSON).toString())
}
}
catch(IllegalStateException e) {
}
}
#OnClose
public void onClose(Session userSession, CloseReason closeReason) {
try {
userSession.close()
}
catch(IllegalStateException e) {
}
}
#OnError
public void onError(Throwable t) {
log.error(t.message, t)
}
private void sendMessage(String message, Session userSession=null) {
wsSession.basicRemote.sendText(message)
}
}
The real magic happens within the onOpen() method. There is where the accessing of the session variable takes place.

EntityManagerFactory closed after context reloaded with #DirtiesContext

I have a Spring Boot app which uses JMS to connect to a queue and listen for incoming messages. In the app I have an integration test which sends some messages to a queue, then makes sure that the things that are supposed to happen when the listener picks up a new message actually happen.
I have annotated my test class with #DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
to ensure my database is clean after each test. Each test passes when it is run in isolation. However when running them all together after the first test passes successfully the next test fails with the exception below when the code under test attempts to save an entity to the database:
org.springframework.transaction.CannotCreateTransactionException: Could not open JPA EntityManager for transaction; nested exception is java.lang.IllegalStateException: EntityManagerFactory is closed
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:431) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:447) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:277) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) ~[spring-tx-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) ~[spring-aop-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at com.sun.proxy.$Proxy95.handleWorkflowEvent(Unknown Source) ~[na:na]
at com.mottmac.processflow.infra.jms.EventListener.onWorkflowEvent(EventListener.java:51) ~[classes/:na]
at com.mottmac.processflow.infra.jms.EventListener.onMessage(EventListener.java:61) ~[classes/:na]
at org.apache.activemq.ActiveMQMessageConsumer.dispatch(ActiveMQMessageConsumer.java:1401) [activemq-client-5.14.3.jar:5.14.3]
at org.apache.activemq.ActiveMQSessionExecutor.dispatch(ActiveMQSessionExecutor.java:131) [activemq-client-5.14.3.jar:5.14.3]
at org.apache.activemq.ActiveMQSessionExecutor.iterate(ActiveMQSessionExecutor.java:202) [activemq-client-5.14.3.jar:5.14.3]
at org.apache.activemq.thread.PooledTaskRunner.runTask(PooledTaskRunner.java:133) [activemq-client-5.14.3.jar:5.14.3]
at org.apache.activemq.thread.PooledTaskRunner$1.run(PooledTaskRunner.java:48) [activemq-client-5.14.3.jar:5.14.3]
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [na:1.8.0_77]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.8.0_77]
at java.lang.Thread.run(Unknown Source) [na:1.8.0_77]
Caused by: java.lang.IllegalStateException: EntityManagerFactory is closed
at org.hibernate.jpa.internal.EntityManagerFactoryImpl.validateNotClosed(EntityManagerFactoryImpl.java:367) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.jpa.internal.EntityManagerFactoryImpl.internalCreateEntityManager(EntityManagerFactoryImpl.java:316) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
at org.hibernate.jpa.internal.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:286) ~[hibernate-entitymanager-5.0.11.Final.jar:5.0.11.Final]
at org.springframework.orm.jpa.JpaTransactionManager.createEntityManagerForTransaction(JpaTransactionManager.java:449) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:369) ~[spring-orm-4.3.6.RELEASE.jar:4.3.6.RELEASE]
... 17 common frames omitted
My test class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { TestGovernance.class })
#DirtiesContext(classMode=ClassMode.AFTER_EACH_TEST_METHOD)
public class ActivitiIntegrationTest
{
private static final String TEST_PROCESS_KEY = "oneTaskProcess";
private static final String FIRST_TASK_KEY = "theTask";
private static final String NEXT_TASK_KEY = "nextTask";
#Autowired
private JmsTemplate jms;
#Autowired
private WorkflowEventRepository eventRepository;
#Autowired
private TaskService taskService;
#Test
public void workFlowEventForRunningTaskMovesItToTheNextStage() throws InterruptedException
{
sendMessageToCreateNewInstanceOfProcess(TEST_PROCESS_KEY);
Task activeTask = getActiveTask();
assertThat(activeTask.getTaskDefinitionKey(), is(FIRST_TASK_KEY));
sendMessageToUpdateExistingTask(activeTask.getProcessInstanceId(), FIRST_TASK_KEY);
Task nextTask = getActiveTask();
assertThat(nextTask.getTaskDefinitionKey(), is(NEXT_TASK_KEY));
}
#Test
public void newWorkflowEventIsSavedToDatabaseAndKicksOffTask() throws InterruptedException
{
sendMessageToCreateNewInstanceOfProcess(TEST_PROCESS_KEY);
assertThat(eventRepository.findAll(), hasSize(1));
}
#Test
public void newWorkflowEventKicksOffTask() throws InterruptedException
{
sendMessageToCreateNewInstanceOfProcess(TEST_PROCESS_KEY);
Task activeTask = getActiveTask();
assertThat(activeTask.getTaskDefinitionKey(), is(FIRST_TASK_KEY));
}
private void sendMessageToUpdateExistingTask(String processId, String event) throws InterruptedException
{
WorkflowEvent message = new WorkflowEvent();
message.setRaisedDt(ZonedDateTime.now());
message.setEvent(event);
// Existing
message.setIdWorkflowInstance(processId);
jms.convertAndSend("workflow", message);
Thread.sleep(5000);
}
private void sendMessageToCreateNewInstanceOfProcess(String event) throws InterruptedException
{
WorkflowEvent message = new WorkflowEvent();
message.setRaisedDt(ZonedDateTime.now());
message.setEvent(event);
jms.convertAndSend("workflow", message);
Thread.sleep(5000);
}
private Task getActiveTask()
{
// For some reason the tasks in the task service are hanging around even
// though the context is being reloaded. This means we have to get the
// ID of the only task in the database (since it has been cleaned
// properly) and use it to look up the task.
WorkflowEvent workflowEvent = eventRepository.findAll().get(0);
Task activeTask = taskService.createTaskQuery().processInstanceId(workflowEvent.getIdWorkflowInstance().toString()).singleResult();
return activeTask;
}
}
The method that throws the exception in the application (repository is just a standard Spring Data CrudRepository):
#Override
#Transactional
public void handleWorkflowEvent(WorkflowEvent event)
{
try
{
logger.info("Handling workflow event[{}]", event);
// Exception is thrown here:
repository.save(event);
logger.info("Saved event to the database [{}]", event);
if(event.getIdWorkflowInstance() == null)
{
String newWorkflow = engine.newWorkflow(event.getEvent(), event.getVariables());
event.setIdWorkflowInstance(newWorkflow);
}
else
{
engine.moveToNextStage(event.getIdWorkflowInstance(), event.getEvent(), event.getVariables());
}
}
catch (Exception e)
{
logger.error("Error while handling workflow event:" , e);
}
}
My test configuration class:
#SpringBootApplication
#EnableJms
#TestConfiguration
public class TestGovernance
{
private static final String WORKFLOW_QUEUE_NAME = "workflow";
#Bean
public ConnectionFactory connectionFactory()
{
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false");
return connectionFactory;
}
#Bean
public EventListenerJmsConnection connection(ConnectionFactory connectionFactory) throws NamingException, JMSException
{
// Look up ConnectionFactory and Queue
Destination destination = new ActiveMQQueue(WORKFLOW_QUEUE_NAME);
// Create Connection
Connection connection = connectionFactory.createConnection();
Session listenerSession = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
MessageConsumer receiver = listenerSession.createConsumer(destination);
EventListenerJmsConnection eventListenerConfig = new EventListenerJmsConnection(receiver, connection);
return eventListenerConfig;
}
}
The JMS message listener (not sure if that will help):
/**
* Provides an endpoint which will listen for new JMS messages carrying
* {#link WorkflowEvent} objects.
*/
#Service
public class EventListener implements MessageListener
{
Logger logger = LoggerFactory.getLogger(EventListener.class);
private WorkflowEventHandler eventHandler;
private MessageConverter messageConverter;
private EventListenerJmsConnection listenerConnection;
#Autowired
public EventListener(EventListenerJmsConnection listenerConnection, WorkflowEventHandler eventHandler, MessageConverter messageConverter)
{
this.eventHandler = eventHandler;
this.messageConverter = messageConverter;
this.listenerConnection = listenerConnection;
}
#PostConstruct
public void setUpConnection() throws NamingException, JMSException
{
listenerConnection.setMessageListener(this);
listenerConnection.start();
}
private void onWorkflowEvent(WorkflowEvent event)
{
logger.info("Recieved new workflow event [{}]", event);
eventHandler.handleWorkflowEvent(event);
}
#Override
public void onMessage(Message message)
{
try
{
message.acknowledge();
WorkflowEvent fromMessage = (WorkflowEvent) messageConverter.fromMessage(message);
onWorkflowEvent((WorkflowEvent) fromMessage);
}
catch (Exception e)
{
logger.error("Error: ", e);
}
}
}
I've tried adding #Transactional' to the test methods and removing it from the code under test and various combinations with no success. I've also tried adding various test execution listeners and I still can't get it to work. If I remove the#DirtiesContext` then the exception goes away and all the tests run without exception (they do however fail with assertion errors as I would expect).
Any help would be greatly appreciated. My searches so far haven't turned up anything, everything suggests that #DirtiesContext should work.
Using #DirtiesContext for this is a terrible idea (imho) what you should do is make your tests #Transactional. I would also suggest to remove the Thread.sleep and use something like awaitility instead.
In theory when you execute a query all pending changes should be committed so you could use awaitility to check for at most 6 seconds to see if something has been persisted in the database. If that doesn't work you can try adding a flush before the query.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { TestGovernance.class })
#Transactional
public class ActivitiIntegrationTest {
private static final String TEST_PROCESS_KEY = "oneTaskProcess";
private static final String FIRST_TASK_KEY = "theTask";
private static final String NEXT_TASK_KEY = "nextTask";
#Autowired
private JmsTemplate jms;
#Autowired
private WorkflowEventRepository eventRepository;
#Autowired
private TaskService taskService;
#Autowired
private EntityManager em;
#Test
public void workFlowEventForRunningTaskMovesItToTheNextStage() throws InterruptedException
{
sendMessageToCreateNewInstanceOfProcess(TEST_PROCESS_KEY);
await().atMost(6, SECONDS).until(getActiveTask() != null);
Task activeTask = getActiveTask());
assertThat(activeTask.getTaskDefinitionKey(), is(FIRST_TASK_KEY));
sendMessageToUpdateExistingTask(activeTask.getProcessInstanceId(), FIRST_TASK_KEY);
Task nextTask = getActiveTask();
assertThat(nextTask.getTaskDefinitionKey(), is(NEXT_TASK_KEY));
}
private Task getActiveTask()
{
em.flush(); // simulate a commit
// For some reason the tasks in the task service are hanging around even
// though the context is being reloaded. This means we have to get the
// ID of the only task in the database (since it has been cleaned
// properly) and use it to look up the task.
WorkflowEvent workflowEvent = eventRepository.findAll().get(0);
Task activeTask = taskService.createTaskQuery().processInstanceId(workflowEvent.getIdWorkflowInstance().toString()).singleResult();
return activeTask;
}
}
You might need / want to polish your getActiveTask a little to be able to return null or maybe this change makes it even behave like you expected it to do.
I just did a single method the others you can probably figure out yourself. Your gain with this approach is probably 2 fold, 1 it will not wait for 5 seconds anymore but less and you don't have to reload your whole application between tests. Both of which should make your tests faster.

Jetty Websocket servlet InstantiationException problems

I'm having problems to make the websocket example of jetty work
public class App {
public static void main(String[] args) {
Server server = new Server(8080);
ServletHandler handler = new ServletHandler();
server.setHandler(handler);
handler.addServletWithMapping(MyEchoServlet.class, "/test/*");
try {
server.start();
server.join();
} catch (Exception e) {
e.printStackTrace();
}
}
public class MyEchoServlet extends WebSocketServlet {
#Override
public void configure(WebSocketServletFactory factory) {
//factory.getPolicy().setIdleTimeout(10000);
factory.register(MyEchoSocket.class);
}
}
#WebSocket(maxMessageSize = 64 * 1024)
public class MyEchoSocket {
Session session;
#OnWebSocketConnect
public void onConnect(Session session){
this.session = session;
System.out.println("init "+session.getUpgradeRequest().getRequestURI().getPath() );
}
#OnWebSocketMessage
public void onText(String msg) {
System.out.println("rec "+msg );
try {
session.getRemote().sendString(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
#OnWebSocketClose
public void onClose(int statusCode, String reason) {
System.out.println("MyWebSocket.onClose()");
}
}
}
gives the following error as soon as I try to create a Websocket from javascript:
var ws = new WebSocket("ws://localhost:8080/test/");
server error:
2013-09-14 15:57:04.076 INFO Server - jetty-9.0.5.v20130815
2013-09-14 15:57:04.122 INFO ServerConnector - Started ServerConnector#1a51ce0{HTTP/1.1}{0.0.0.0:8080}
2013-09-14 15:57:11.700 WARN ContextHandler - unavailable
java.lang.InstantiationException: app.App$MyEchoServlet
at java.lang.Class.newInstance(Unknown Source)
at org.eclipse.jetty.servlet.ServletHolder.newInstance(ServletHolder.java:979)
at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:523)
at org.eclipse.jetty.servlet.ServletHolder.getServlet(ServletHolder.java:424)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:671)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:505)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:434)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:136)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:445)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:268)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:229)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:358)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:601)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:532)
at java.lang.Thread.run(Unknown Source)
2013-09-14 15:57:11.706 WARN ServletHandler -
javax.servlet.ServletException: app.App$MyEchoServlet-4598284#e19c6ca5==app.App$MyEchoServlet,-1,false
at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:563)
at org.eclipse.jetty.servlet.ServletHolder.getServlet(ServletHolder.java:424)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:671)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:505)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:434)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:136)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:97)
at org.eclipse.jetty.server.Server.handle(Server.java:445)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:268)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:229)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.run(AbstractConnection.java:358)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:601)
at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:532)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.InstantiationException: app.App$MyEchoServlet
at java.lang.Class.newInstance(Unknown Source)
at org.eclipse.jetty.servlet.ServletHolder.newInstance(ServletHolder.java:979)
at org.eclipse.jetty.servlet.ServletHolder.initServlet(ServletHolder.java:523)
... 13 more
jetty version: 9.0.5v20130815 (from maven eclipse.jetty repo)
Since you have that declared both your servlet and socket as inner classes, change the following 2 lines of your code ...
// From
public class MyEchoServlet extends WebSocketServlet {
// To
public static class MyEchoServlet extends WebSocketServlet {
// From
public class MyEchoSocket {
// To
public static class MyEchoSocket {
This is because Java cannot just instantiate a non-static inner class.
Try this from your main() method and you'll see ...
Servlet test = new MyEchoServlet();

WSSecurityEngine says password was not supplied during callback even though it is provided

I'm in the process of creating a client for a web service. I keep getting the following error:
AxisFault
faultCode: {http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}InvalidSecurity
faultSubcode:
faultString:
Security Data : General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
faultActor:
faultNode:
faultDetail: {http://xml.apache.org/axis/}
stackTrace:
Security Data : General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
This is my environment here:
Intellij IDEA IDE
Apache Axis
Apache WSS4J 1.5.1
Apache XML Security 1.4.0
JDK 1.6
Max OS X
Although searching on the internet provides a lot of examples of how the security header can be added to the request via XML configuration, my requirement is to do this dynamically via the program. So here is my code:
public class AxisClient implements CallbackHandler {
ServerEnvironment environment;
AxisClient(ServerEnvironment environment) {
this.environment = environment;
}
public enum ServerEnvironment {
LIVE("https://ics2ws.ic3.com/commerce/1.x/transactionProcessor"),
TEST("https://ics2wstest.ic3.com/commerce/1.x/transactionProcessor");
String url;
ServerEnvironment (String url) {
this.url = url;
}
public String getUrl() {
return url;
}
}
public enum Merchant {
TestMerchant ("testpassword");
private String transactionKey;
Merchant(String transactionKey) {
this.transactionKey = transactionKey;
}
public String getTransactionKey() {
return transactionKey;
}
}
public static void main(String[] argv) {
String ani = "7162502800";
String zipCode = "14221";
String ccNum ="5555555555554444";
String expMonth = "01";
String expYear = "15";
String cvv = "123";
String unitPrice = "9.99";
String qty = "2";
try {
new AxisClient(ServerEnvironment.TEST).doAuth(Merchant.TestMerchant, ani, zipCode, ccNum, expMonth, expYear, cvv, String.valueOf(new Date().getTime()), unitPrice, qty);
}
catch (Exception e) {
e.printStackTrace();
}
}
public Boolean doAuth(Merchant merchant, String ani, String zipCode, String ccNum, String expMonth, String expYear, String cvv, String id, String unitPrice, String qty) throws Exception {
Boolean result = false;
RequestMessage request;
BillTo billTo;
Card card;
PurchaseTotals purchaseTotals;
Item item;
Item[] items;
ReplyMessage reply;
try {
// billing info
billTo = new BillTo();
billTo.setPhoneNumber(ani);
billTo.setPostalCode(zipCode);
// card info
card = new Card();
card.setAccountNumber(ccNum);
card.setExpirationMonth(new BigInteger(expMonth));
card.setExpirationYear(new BigInteger(expYear));
card.setCvNumber(cvv);
// currency info
purchaseTotals = new PurchaseTotals();
purchaseTotals.setCurrency("USD");
// item
item = new Item();
item.setId(new BigInteger(id));
item.setUnitPrice(unitPrice);
item.setQuantity(new BigInteger(qty));
// add item to items array
items = new Item[1];
items[0] = item;
// create our request
request = new RequestMessage();
request.setMerchantID(merchant.toString());
request.setCcAuthService(new CCAuthService());
request.getCcAuthService().setRun("true");
// add request specific params
request.setBillTo(billTo);
request.setCard(card);
request.setPurchaseTotals(purchaseTotals);
request.setItem(items);
reply = post(merchant, request);
if (reply != null) {
System.out.println(ReflectionToStringBuilder.toString(reply, ToStringStyle.MULTI_LINE_STYLE));
}
}
catch (Exception e) {
throw e;
}
return result;
}
public EngineConfiguration createConfigurationWithSecurityHeaders(Merchant merchant) throws Exception {
SimpleProvider result;
Handler securityHandler;
SimpleChain requestHandler;
SimpleChain responseHandler;
Handler pivot;
Handler transport;
try {
result = new SimpleProvider();
securityHandler = new WSDoAllSender();
securityHandler.setOption(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
securityHandler.setOption(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PASSWORD_TEXT);
securityHandler.setOption(WSHandlerConstants.PW_CALLBACK_REF, this);
securityHandler.setOption(WSHandlerConstants.USER, merchant.toString());
securityHandler.setOption(WSHandlerConstants.MUST_UNDERSTAND, "false");
requestHandler = new SimpleChain();
requestHandler.addHandler(securityHandler);
responseHandler = new SimpleChain();
responseHandler.addHandler(securityHandler);
pivot = new HTTPSender();
transport = new SimpleTargetedChain(requestHandler, pivot, responseHandler);
result.deployTransport(HTTPTransport.DEFAULT_TRANSPORT_NAME, transport);
}
catch (Exception e) {
throw e;
}
return result;
}
public ReplyMessage post (Merchant merchant, RequestMessage request) throws Exception {
ReplyMessage result;
TransactionProcessorLocator locator;
URL endPoint;
ITransactionProcessorStub stub;
EngineConfiguration configuration;
try {
locator = new TransactionProcessorLocator();
// use client config
configuration = createConfigurationWithSecurityHeaders(merchant);
locator.setEngineConfiguration(configuration);
locator.setEngine(new org.apache.axis.client.AxisClient(configuration));
endPoint = new URL(environment.getUrl());
stub = (ITransactionProcessorStub) locator.getportXML(endPoint);
stub._setProperty(WSHandlerConstants.USER, request.getMerchantID());
stub._setProperty(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
stub._setProperty(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PASSWORD_TEXT);
stub._setProperty(WSHandlerConstants.PW_CALLBACK_REF, this);
stub._setProperty(WSHandlerConstants.MUST_UNDERSTAND, "false");
result = stub.runTransaction(request);
}
catch (Exception e) {
throw e;
}
return result;
}
#Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (Callback callback : callbacks) {
System.out.println(ReflectionToStringBuilder.toString(callback, ToStringStyle.MULTI_LINE_STYLE));
if (callback instanceof WSPasswordCallback) {
WSPasswordCallback passwordCallback = (WSPasswordCallback) callback;
switch (Merchant.valueOf(passwordCallback.getIdentifer())) {
case TestMerchant:
passwordCallback.setPassword(Merchant.TestMerchant.getTransactionKey());
System.out.println(ReflectionToStringBuilder.toString(passwordCallback, ToStringStyle.MULTI_LINE_STYLE));
break;
default:
throw new UnsupportedCallbackException(callback, "Unrecognized prompt!");
}
}
else {
throw new UnsupportedCallbackException(callback, "Unrecognized callback!");
}
}
}
}
As you can see from the above, my class implements CallbackHandler and I'm overriding handle() which provides the password for WSPasswordCallback.
Here is the output of the print statement for when I get the callback first:
org.apache.ws.security.WSPasswordCallback#50c713d2[
identifier=TestMerchant
password=<null>
key=<null>
usage=2
passwordType=<null>
]
Here is the output for after setting the password:
org.apache.ws.security.WSPasswordCallback#50c713d2[
identifier=TestMerchant
password=testpassword
key=<null>
usage=2
passwordType=<null>
]
So I'm not really sure why I keep getting that error message. Any help in solving this issue will be greatly appreciated.
Also recommendations for a different approach (axis2, cxf) are welcome.
Here is my full stack trace if it is of any help:
AxisFault
faultCode: {http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}InvalidSecurity
faultSubcode:
faultString:
Security Data : General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
faultActor:
faultNode:
faultDetail:
{http://xml.apache.org/axis/}stackTrace:
Security Data : General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
at org.apache.axis.message.SOAPFaultBuilder.createFault(SOAPFaultBuilder.java:222)
at org.apache.axis.message.SOAPFaultBuilder.endElement(SOAPFaultBuilder.java:129)
at org.apache.axis.encoding.DeserializationContext.endElement(DeserializationContext.java:1087)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:601)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1782)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2939)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:648)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:140)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:511)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:808)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:119)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
at javax.xml.parsers.SAXParser.parse(SAXParser.java:395)
at org.apache.axis.encoding.DeserializationContext.parse(DeserializationContext.java:227)
at org.apache.axis.SOAPPart.getAsSOAPEnvelope(SOAPPart.java:696)
at org.apache.axis.Message.getSOAPEnvelope(Message.java:435)
at org.apache.axis.handlers.soap.MustUnderstandChecker.invoke(MustUnderstandChecker.java:62)
at org.apache.axis.client.AxisClient.invoke(AxisClient.java:206)
at org.apache.axis.client.Call.invokeEngine(Call.java:2784)
at org.apache.axis.client.Call.invoke(Call.java:2767)
at org.apache.axis.client.Call.invoke(Call.java:2443)
at org.apache.axis.client.Call.invoke(Call.java:2366)
at org.apache.axis.client.Call.invoke(Call.java:1812)
at itg.cybersource.axis.ITransactionProcessorStub.runTransaction(ITransactionProcessorStub.java:1284)
at itg.AxisClient.post(AxisClient.java:208)
at itg.AxisClient.doAuth(AxisClient.java:132)
at itg.AxisClient.main(AxisClient.java:75)
{http://xml.apache.org/axis/}hostname:C02GD302DRJL.local
Security Data : General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
General security error (WSSecurityEngine: Callback supplied no password for: TestMerchant)
at org.apache.axis.message.SOAPFaultBuilder.createFault(SOAPFaultBuilder.java:222)
at org.apache.axis.message.SOAPFaultBuilder.endElement(SOAPFaultBuilder.java:129)
at org.apache.axis.encoding.DeserializationContext.endElement(DeserializationContext.java:1087)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.endElement(AbstractSAXParser.java:601)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanEndElement(XMLDocumentFragmentScannerImpl.java:1782)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl$FragmentContentDriver.next(XMLDocumentFragmentScannerImpl.java:2939)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentScannerImpl.next(XMLDocumentScannerImpl.java:648)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocumentScannerImpl.next(XMLNSDocumentScannerImpl.java:140)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:511)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:808)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:737)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:119)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1205)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
at javax.xml.parsers.SAXParser.parse(SAXParser.java:395)
at org.apache.axis.encoding.DeserializationContext.parse(DeserializationContext.java:227)
at org.apache.axis.SOAPPart.getAsSOAPEnvelope(SOAPPart.java:696)
at org.apache.axis.Message.getSOAPEnvelope(Message.java:435)
at org.apache.axis.handlers.soap.MustUnderstandChecker.invoke(MustUnderstandChecker.java:62)
at org.apache.axis.client.AxisClient.invoke(AxisClient.java:206)
at org.apache.axis.client.Call.invokeEngine(Call.java:2784)
at org.apache.axis.client.Call.invoke(Call.java:2767)
at org.apache.axis.client.Call.invoke(Call.java:2443)
at org.apache.axis.client.Call.invoke(Call.java:2366)
at org.apache.axis.client.Call.invoke(Call.java:1812)
at itg.cybersource.axis.ITransactionProcessorStub.runTransaction(ITransactionProcessorStub.java:1284)
at itg.AxisClient.post(AxisClient.java:208)
at itg.AxisClient.doAuth(AxisClient.java:132)
at itg.AxisClient.main(AxisClient.java:75)
After many days of trying very many different things, I finally figured out the answer to my problem. Talk about the problem being in between the keyboard and the chair!!!!!
So without further ado here is what the issue was:
securityHandler.setOption(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PASSWORD_TEXT);
Now WSConstants.PASSWORD_TEXT actually equates to "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText". What I must have really been using is WSConstants.PW_TEXT which equates to "PasswordText" which is exactly what I needed in my code. Once I made the change, everything works beautifully.
Contrary to popular belief, you can do all this entirely in a programmatic manner. You DO NOT need to configure the WSDD xml for you to intercept a message and handle WS-Security in the SOAP header. To tidy up loose ends, here is what the modified methods look like:
public EngineConfiguration createConfigurationWithSecurityHeaders(Merchant merchant) throws Exception {
SimpleProvider result;
Handler securityHandler;
SimpleChain requestHandler;
SimpleChain responseHandler;
Handler pivot;
Handler transport;
try {
result = new SimpleProvider();
securityHandler = new WSDoAllSender();
securityHandler.setOption(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN);
securityHandler.setOption(WSHandlerConstants.PASSWORD_TYPE, WSConstants.PW_TEXT);
securityHandler.setOption(WSHandlerConstants.USER, merchant.toString());
securityHandler.setOption(WSHandlerConstants.MUST_UNDERSTAND, "false");
requestHandler = new SimpleChain();
requestHandler.addHandler(securityHandler);
responseHandler = new SimpleChain();
responseHandler.addHandler(securityHandler);
pivot = new HTTPSender();
transport = new SimpleTargetedChain(requestHandler, pivot, responseHandler);
result.deployTransport(HTTPTransport.DEFAULT_TRANSPORT_NAME, transport);
}
catch (Exception e) {
throw e;
}
return result;
}
public ReplyMessage post (Merchant merchant, RequestMessage request) throws Exception {
ReplyMessage result;
TransactionProcessorLocator locator;
URL endPoint;
ITransactionProcessorStub stub;
EngineConfiguration configuration;
try {
locator = new TransactionProcessorLocator();
// use client config
configuration = createConfigurationWithSecurityHeaders(merchant);
locator.setEngineConfiguration(configuration);
locator.setEngine(new org.apache.axis.client.AxisClient(configuration));
endPoint = new URL(environment.getUrl());
stub = (ITransactionProcessorStub) locator.getportXML(endPoint);
stub._setProperty(WSHandlerConstants.PW_CALLBACK_REF, this);
result = stub.runTransaction(request);
}
catch (Exception e) {
throw e;
}
return result;
}
Once these modifications are made, your client will work. Please keep in mind that a few of the settings above are specific to the service I'm integrating with. You might have to tweak these to suit your integration which might require a little trial and error.
Thanks again to all the people who post incredibly knowledgeable articles in SO enabling users like me solve problems we come across every now and then.

Categories