How to pass data to EJBContext on client side? - java

I have following interceptor on server:
#Resource
EJBContext ejbContext;
#AroundInvoke
public Object onInvocation( InvocationContext aInvocationContext ) throws Exception
{
final Object myValue = ejbContext.getContextData().get("MyKey");
...
Object proceed = aInvocationContext.proceed();
...
return proceed;
}
How to pass data ("MyKey") to EJBContext on client side?
I tried to lookup it but I get javax.naming.InvalidNameException' exception during lookup or EJBCLIENT000409: No more destinations are available when I call getContextData(). I tried several ways I do not know If I do something wrong or EJBContext is some special resource and It is not possible to modify it on client.
How lookup name should look like?I tried java:comp/EJBContext, appName/moduleName/EJBContext.

I created client interceptor.
EJBClientContext.requireCurrent().registerInterceptor( 0, new MyClientInterceptor() );
There is important note in registerInterceptor method javadoc:
Note: If an interceptor is added or removed after a proxy is used,
this will not affect the proxy interceptor list.
MyClientInterceptor:
import org.jboss.ejb.client.EJBClientInterceptor;
import org.jboss.ejb.client.EJBClientInvocationContext;
public class MyClientInterceptor implements EJBClientInterceptor
{
#Override
public void handleInvocation( EJBClientInvocationContext aContext ) throws Exception
{
Object myValue =...
aContext.getContextData().put( "MyKey", myValue );
aContext.sendRequest();
}
#Override
public Object handleInvocationResult( EJBClientInvocationContext aContext ) throws Exception
{
return aContext.getResult();
}
}

Related

Catch Return Value of An Interceptor

I would like to retrieve the return value of this interceptor:
https://arjan-tijms.omnifaces.org/2012/01/cdi-based-asynchronous-alternative.html
#Interceptor
#Asynchronous
#Priority(PLATFORM_BEFORE)
public class AsynchronousInterceptor implements Serializable {
private static final long serialVersionUID = 1L;
#Resource
private ManagedExecutorService managedExecutorService;
private static final ThreadLocal<Boolean> asyncInvocation = new ThreadLocal<Boolean>();
#AroundInvoke
public synchronized Object submitAsync(InvocationContext ctx) throws Exception {
if (TRUE.equals(asyncInvocation.get())) {
return ctx.proceed();
}
return new FutureDelegator(managedExecutorService.submit( ()-> {
try {
asyncInvocation.set(TRUE);
return ctx.proceed();
} finally {
asyncInvocation.remove();
}
}));
}
}
here is a CdiBean of mine profiting from AsynchronousInterceptor by letting data be loaded async..
public class SomeCDI {
#Asynchronous
public void loadDataAsync() {....}
}
this is how I use the cdi bean later in code:
#Inject
SomeCDI dataLoader;
dataLoader.loadDataAsync(); // the loading starts async but I never find out when is the Future class done???
so my question is how to retrieve return value (in my example from FutureDelegator)???
You won't. Asynchronous invocations on EJB and in the model suggested by Tijms are "fire and forget": you invoke them and let them do their job. Eventually, you can make the async method fire some event when it ends to "return" the result, observing this event to give user some response (websockets, maybe?).
Ideally, the asynchronous method should be void and do some callback lift.
Note that CDI 2.0 event model has the fireAsync method, which should be used instead of your own implementation, as it already have the proper contexts and can be enriched by transaction markers and custom options (when using NotificationOptions method signature).

Inject WebSocket in service REST

I need to send a message a client, after the creation of item. The item is created an ApiRest. Then I created my WebSocket with #ApplicationScope and I Injected in serviceREST with #Inject. The Problem is when the webSocket was initialized, in my serviceRest this webSocket's session still is null.
How I can use the web SOcket in my apirest?
#Path("citas")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class citaResource {
#Inject
com.softcase.citasmanager.websocket.ws websocket;
#GET
#Path("cita")
#Produces("application/json")
public Response cita() {
websocket.onMessage("Your Item was created");//Session of webSocket is null
return Response.ok("ItemCreated", MediaType.APPLICATION_JSON).build();
}
}
#ApplicationScope
#ServerEndpoint("/item")
public class ws{
private Session session;
#OnOpen
public void open(Session session) {
this.session = session;
}
#OnMessage
public void onMessage(String message) {
this.session.getBasicRemote().sendText(message);
}
A little context
Instances: there is a unique Session instance per client-server pair i.e. one instance of Session is created for a each client which connects to the WebSocket server endpoint. In short, the number of unique Session instances is equal to number of connected clients
Source: https://abhirockzz.gitbooks.io/java-websocket-api-handbook/content/lifecycle_and_concurrency_semantics.html
For more details: https://tyrus-project.github.io/documentation/1.13.1/index/lifecycle.html
A suggestion is to use a static variables like
// #ApplicationScope
#ServerEndpoint("/item")
public class ws{
// something like
private static final Set<javax.websocket.Session> ALL_SESSIONS = new HashSet<>();
// ...
}
An example can be found here. It's an option but I do not think it solves your injection issue.
Another option is to leverage the javax.websocket.Session#getOpenedSessions() method such as this chat example. But once again, it does not solve the injection issue.
Your example
You are using both websocket and REST. As I understand, the flow is:
User A, B, C are connected
User A submits a request to citas/cita and receives the REST response
At the same time, A, B, C receive a websocket notification
So, as you wrote, on one hand, you have
#Path("citas")
// ...
public class CitaResource{
// ...
}
and
// #ApplicationScope -> commented as irrelevant in your situation
#ServerEndpoint("/item")
public class ws{
// ...
}
In the example, there is one instance of CitaResource when user A made the request and three instances of ws as A, B, C are connected. However, you were right about the injection: you need to have something injected in CitaResource but you need a bean that is always available and as you noticed, websocket instances are not a good option and which session the container must inject?
A websocket sessions handler
The solution is to use an application scoped bean to handle all the existing sessions. I got it from Oracle tutorial. It goes like this:
// com.softcase.citasmanager.websocket.SessionHandler
#ApplicatedScoped
#Named // optional
public class MySessionHandler{
private final Set<Session> ALL_SESSIONS;
// or use a map if you need to identify the
// the session by a key. This example uses Set
// private final Map<String, Session> ALL_SESSIONS;
public MySessionHandler(){
ALL_SESSIONS = new HashSet<>();
}
// manage sessions
public void addSession(Session session){
this.ALL_SESSIONS.add(session);
}
public void removeSession(Session session){
this.ALL_SESSIONS.remove(session);
}
// send messages to all instances:
public void sendMessage(String message){
this.ALL_SESSIONS.stream()
// optional
.filter(s -> s.isOpen())
// or whatever method you want to send a message
.forEach( s -> s.getBasicRemote().sendText(message);
}
// or if you want to target a specific session
// hence my questions in comments
public void sendMessage(String message, String target){
this.ALL_SESSIONS..stream()
// identity the target session
.filter(s -> s.equals(target))
// optional
.filter(s -> s.isOpen())
.forEach( s -> s.getBasicRemote().sendText(message);
}
}
Note:
I optionally check that the stored session is still opened. isOpen() is not mandatory but it might avoid some errors
Think the session handler as the "captain": it knows everything about the websocket sessions whereas the sessions themselves do not know about each other.
However, you need to adapt your endpoint to make the session handler efficient:
// com.softcase.citasmanager.websocket.WsCita
#ServerEndpoint
public class WsCita{
// there is no need to declare the session as attribute
// private Session session;
// ApplicatedScoped so always defined
#Inject
private MySessionHandler handler;
#OnOpen
public void open(Session session){
handler.addSession(session); // "Aye cap'tain, reporting from duty!"
// your stuff
}
#OnClose
public void close(Session session, CloseReason closeReason){
handler.removeSession(session); // "Cya cap'tain, that's all for today!"
// your stuff
}
// your OnMessage and other stuff here
}
Now we have set our websocket architecture, what now?
You have one instance of WsCita per client. At any time, there might be zero, one or more instances.
MySessionHandler knows this information and is #ApplicatedScoped so it is safe to inject it
The REST endpoint then changes to:
#Path("citas")
// ...
public class citaResource {
#Inject
com.softcase.citasmanager.websocket.SessionHandler handler;
#GET
// ...
public Response cita() {
// REST processing
// ...
// Websocket processing:
// - Handler is always here for you
// - Handler knows which websocket sessions to send the message to.
// The RestController is not aware of the recipients
handler.sendMessage("Your Item was created");
}
}
Please note that I put the websocket processing after the REST processing as you may not always send the message (e.g. creation or whatever exception).
Misc
Unrelated to your questions but I have some comments about your code:
Classes name are CamelCase starting with capitalized letter per Oracle recommendation
Avoid generic name for your classes such as Ws. I renamed it WsCita for the example

Codename One - Externalizable object not supported in web service

I have created a CN1 web service which some custom objects that I want to externalize in order to send over the network. I read through several articles on how to create the web service and how to work with the CN1 Externalizable interface.
This works well for web service methods that return a custom externalizable object, however the only indicator that I have is that a method which takes an externalizable object as an argument, I get the following error:
SCHWERWIEGEND: Servlet.service() for servlet [CN1WebServiceServlet]
in context with path [/<myPath>] threw exception
java.io.IOException: Object type not supported: Post
The object is properly registered with the Util class, as changing either the object ID or commenting out the register call will cause a null pointer instead of the IO exception.
The Post class looks like this (simplified to the minimum which already fails):
public class Post implements Externalizable {
public int postid;
public int userid;
// default constructor needed for web service marshalling
public Post() {
}
#Override
public int getVersion() {
return 1;
}
#Override
public void externalize(DataOutputStream out) throws IOException {
Util.writeUTF("" + postid, out);
Util.writeUTF("" + userid, out);
}
#Override
public void internalize(int version, DataInputStream in) throws IOException {
this.postid = Integer.parseInt(Util.readUTF(in));
this.userid = Integer.parseInt(Util.readUTF(in));
}
#Override
public String getObjectId() {
return "Post";
}
Note that this Post object works well when I call a web service method which returns a post object, but not when I send a Post object to the web service:
// works
public static com.codename1.io.Externalizable getPostDetails(int postid) {
return getPostDetails(postid);
}
// fails
public static void sendPost(com.codename1.io.Externalizable post) {
sendPost(post);
}
I am at a loss of what I missed here.
Thanks and best regards
In your Servlet code call Util.register("Post", Post.class); which should hopefully resolve this.
Thanks a lot Shai! My mistake was to assume that registering the externalizable object on one side only. But of course it needs to be registered wherever it is going to be internalized, so in this case on my server.
Solution:
Within the "CN1WebServiceServlet" (not the ProxyServer class where the rest of the code has to be completed), call Util.register("Post", Post.class);
if(methodName.equals("sendPost")) {
Util.register("Post", Post.class); // this is a my insertedline, rest is generated
Object[] args = ProxyServerHelper.readMethodArguments(di, def_sendPost);
WebServiceProxyServer.sendPost((com.codename1.io.Externalizable)args[0]);
ProxyServerHelper.writeResponse(response, def_sendPost);
return;
}

CXF Interceptor tied up to web method does not work

I need to catch exception of business logic and cut down its message (stack trace of exception, I don't want to log a bunch of stack trace, just exception type and its message). According to the examples in internet, I have to override or implement handleFault(Message message) method. I have web method for which I attached Interceptor class implements org.apache.cxf.interceptor.Interceptor. But It doesn't work. There is no sign of its call. I can't find any solution surfing through internet.
My web service :
#Stateless
#WebService(name = "LocationServicesWS", targetNamespace = "http://com.example.apps.sc.ws", serviceName = "locationServices")
#WebContext(contextRoot = "/bb/sc", urlPattern = "/lb", transportGuarantee = "NONE", secureWSDLAccess = false)
#Pool("sl-strict-pool")
public class LbServices implements IServicesLocal, IServicesRemote {
#WebMethod
#Override
#Interceptors(TestFaultInterceptor.class)
public LbLocation getLbsLocationService(#WebParam(name="ms")String ms, #WebParam(name="serID")Long ser) throws ScAccViolation, LbsException {
return serviceProcesses.getLbsLocationForService(ms, ser);
}
}
My custom interceptor:
#Interceptor
#Priority(Interceptor.Priority.APPLICATION)
public class TestFaultInterceptor implements org.apache.cxf.interceptor.Interceptor {
private static Logger logger = Logger.getLogger(String.valueOf(TestFaultInterceptor.class));
#Override
public void handleMessage(Message message) throws Fault {
//this method is not invoked
logger.info("handleMessage");
}
#Override
public void handleFault(Message message) {
//this method is also not invoked
logger.info("handleFault");
}
/*
#AroundInvoke
public Object intercept(InvocationContext invocationContext) throws Exception {
// This method works fine.
return invocationContext.proceed();
}
*/
But in my Interceptor class when I don't implement Interceptor and put annotation #AroundInvoke it works. So what's the problem ? Is there any solution without creating additional *.xml files ?
I have even put annotation
#Priority(Interceptor.Priority.APPLICATION)
But it's to no avail.
Here are links I stumbled in the internet (especially how to not log whole stack trace).
Apache CXF JAX-WS return fault without logging exception
How to get incoming & outgoing soap xml in a simple way using Apache CXF?

How to change the Validation Error behaviour for Dropwizard?

In Dropwizard I use #Valid annotations for my resource methods:
public class Address {
#NotNull
String street
...
}
#Path("/address")
#Produces(MediaType.APPLICATION_JSON)
public class AddressResource {
#POST
public MyResponse addAddress(#Valid Address address) {
if (address == null) {
throw new WebApplicationException("address was null");
}
...
}
}
On application start I register a custom WebApplicationExceptionMapper which handles WebApplicationExceptions. Thus, for addresses with the value null, the exception is thrown and handled in the mapper which generates a useful response. However, if the address is not null but street is, Dropwizard automatically generates a response and sends it to the client (which I dislike).
How do I interfere this response so that in the end it is also handled by the mapper?
Dropwizard registers their own constraint violation exception mapper which you can override.
Since Jersey does not yet support the #Priority annotation on the exception mappers (https://java.net/jira/browse/JERSEY-2437), you should disable the registration of the Dropwizard's mappers before register your own. Here is an fragment of the Application's run method and an exception mapper:
#Override
public void run(
final Configuration config,
final Environment environment) throws Exception {
((DefaultServerFactory)config.getServerFactory()).setRegisterDefaultExceptionMappers(false);
// Register custom mapper
environment.jersey().register(new MyConstraintViolationExceptionMapper());
// Restore Dropwizard's exception mappers
environment.jersey().register(new LoggingExceptionMapper<Throwable>() {});
environment.jersey().register(new JsonProcessingExceptionMapper());
environment.jersey().register(new EarlyEofExceptionMapper());
...
}
#Provider
public class MyConstraintViolationExceptionMapper
implements ExceptionMapper<ConstraintViolationException> {
#Override
public Response toResponse(ConstraintViolationException exception) {
...
}
}
In newer Dropwizard versions (for e.g. 0.9.2), I had to do:
env.jersey().register(new JsonProcessingExceptionMapper(true));

Categories

Resources