How to get active websocket sessions in tomcat programatically? - session

I am running tomcat 8 to terminate websocket connections. I want to get all active websocket sessions say for an endpoint. I know that if you have one session object you can call getOpenSessions() to get all sessions, but the point is that I do not have access to a session object from where I need to get all sessions in code.

Wow, no answers after nearly a year! I stumbled across this while searching for some clues to an issue I'm having on Tomcat 8 with getOpenSessions(). For my problem, I ended up doing the below, which would also solve this issue. In general, just have a static map that you populate in open and remove from in close:
#ServerEndpoint(value="/msg/{owner}", encoders=MessageEncoder.class, decoders=MessageEncoder.class)
public class WebSocketListener {
private static final Logger logger = LoggerFactory.getLogger(WebSocketListener.class);
private static Map<String, Session> sessions = new HashMap<String, Session>();
public WebSocketListener() {
System.out.println("created");
}
#OnOpen
public void open(Session session, #PathParam("owner") String owner) {
System.out.println("open "+owner);
sessions.put(session.getId(), session);
session.getUserProperties().put("owner", owner);
System.out.println("open");
if (session.getUserPrincipal() != null) {
session.getUserProperties().put("owner", owner);
}
else {
try {
session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "Not authorized"));
} catch (IOException e) {
}
}
}
#OnClose
public void close(Session session) {
System.out.println("close");
sessions.remove(session.getId());
}
#OnError
public void onError(Throwable error) {
logger.error("",error);
}
#OnMessage
public void onMessage(final Session session, final Message message) {
System.out.println("onMessage");
String owner = (String)session.getUserProperties().get("owner");
Long appId = message.getAppId();
for (Session s:sessions.values()) {
System.out.println(s);
if (s.isOpen() && (message.isEcho() || s != session) && owner.equals(s.getUserProperties().get("owner")) && (appId == null || appId.equals(s.getUserProperties().get("appId")))) {
s.getAsyncRemote().sendObject(message);
}
}
}
}

Related

Building a Future API on top of Netty

I want to build an API based on Futures (from java.util.concurrent) that is powered by a custom protocol on top of Netty (version 4). Basic idea is to write a simple library that would abstract the underlying Netty implementation and make it easier to make requests.
Using this library, one should be able to write something like this:
Request req = new Request(...);
Future<Response> responseFuture = new ServerIFace(host, port).call(req);
// For example, let's block until this future is resolved
Reponse res = responseFuture.get().getResult();
Underneath this code, a Netty client is connected
public class ServerIFace {
private Bootstrap bootstrap;
private EventLoopGroup workerGroup;
private String host;
private int port;
public ServerIFace(String host, int port) {
this.host = host;
this.port = port;
this.workerGroup = new NioEventLoopGroup();
bootstrap();
}
private void bootstrap() {
bootstrap = new Bootstrap();
bootstrap.group(workerGroup);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ObjectEncoder());
ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(Response.class.getClassLoader())));
ch.pipeline().addLast("response", new ResponseReceiverChannelHandler());
}
});
}
public Future<Response> call(final Request request) throws InterruptedException {
CompletableFuture<Response> responseFuture = new CompletableFuture<>();
Channel ch = bootstrap.connect(host, port).sync().channel();
ch.writeAndFlush(request).addListener((f) -> {
if (f.isSuccess()) {
System.out.println("Wrote successfully");
} else {
f.cause().printStackTrace();
}
});
ChannelFuture closeFuture = ch.closeFuture();
// Have to 'convert' ChannelFuture to java.util.concurrent.Future
closeFuture.addListener((f) -> {
if (f.isSuccess()) {
// How to get this response?
Response response = ((ResponseReceiverChannelHandler) ch.pipeline().get("response")).getResponse();
responseFuture.complete(response);
} else {
f.cause().printStackTrace();
responseFuture.cancel(true);
}
ch.close();
}).sync();
return responseFuture;
}
}
Now, as you can see, in order to abstract Netty's inner ChannelFuture, I have to 'convert' it to Java's Future (I'm aware that ChannelFuture is derived from Future, but that information doesn't seem useful at this point).
Right now, I'm capturing this Response object in the last handler of my inbound part of the client pipeline, the ResponseReceiverChannelHandler.
public class ResponseReceiverChannelHandler extends ChannelInboundHandlerAdapter {
private Response response = null;
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
this.response = (Response)msg;
ctx.close();
}
public Response getResponse() {
return response;
}
}
Since I'm new to Netty and these things in general, I'm looking for a cleaner, thread-safe way of delivering this object to the API user.
Correct me if I'm wrong, but none of the Netty examples show how to achieve this, and most of the Client examples just print out whatever they get from Server.
Please note that my main goal here is to learn more about Netty, and that this code has no production purposes.
For the reference (although I don't think it's that relevant) here's the Server code.
public class Server {
public static class RequestProcessorHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ChannelFuture future;
if (msg instanceof Request) {
Request req = (Request)msg;
Response res = some function of req
future = ctx.writeAndFlush(res);
} else {
future = ctx.writeAndFlush("Error, not a request!");
}
future.addListener((f) -> {
if (f.isSuccess()) {
System.out.println("Response sent!");
} else {
System.out.println("Response not sent!");
f.cause().printStackTrace();
}
});
}
}
public int port;
public Server(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(Request.class.getClassLoader())));
ch.pipeline().addLast(new ObjectEncoder());
// Not really shutting down this threadpool but it's ok for now
ch.pipeline().addLast(new DefaultEventExecutorGroup(2), new RequestProcessorHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true);
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new Server(port).run();
}
}

Http proxy in Netty websocket client to connect to internet

My application is running behind a corporate firewall and I need to use http proxy(http://theclientproxy.net:8080) to connect to internet
I have used the Netty client as below,
https://github.com/netty/netty/tree/4.1/example/src/main/java/io/netty/example/http/websocketx/client
Code:
public final class WebSocketClient {
static final String URL = System.getProperty("url", "wss://127.0.0.1:8080/websocket");
public static void main(String[] args) throws Exception {
URI uri = new URI(URL);
String scheme = uri.getScheme() == null? "ws" : uri.getScheme();
final String host = uri.getHost() == null? "127.0.0.1" : uri.getHost();
final int port;
final boolean ssl = "wss".equalsIgnoreCase(scheme);
final SslContext sslCtx;
if (ssl) {
sslCtx = SslContextBuilder.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE).build();
} else {
sslCtx = null;
}
EventLoopGroup group = new NioEventLoopGroup();
try {
final WebSocketClientHandler handler =
new WebSocketClientHandler(
WebSocketClientHandshakerFactory.newHandshaker(
uri, WebSocketVersion.V13, null, true, new DefaultHttpHeaders()));
Bootstrap b = new Bootstrap();
b.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline p = ch.pipeline();
if (sslCtx != null) {
p.addFirst(new HttpProxyHandler(new InetSocketAddress("theclientproxy.net", 8080) ) );
p.addLast(sslCtx.newHandler(ch.alloc(), host, port));
}
p.addLast(
new HttpClientCodec(),
new HttpObjectAggregator(8192),
WebSocketClientCompressionHandler.INSTANCE,
handler);
}
});
Channel ch = b.connect(uri.getHost(), port).sync().channel();
handler.handshakeFuture().sync();
BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String msg = console.readLine(); //THIS IS NULL IN DATA CENTER LOGS
if (msg == null) {
break;
} else if ("bye".equals(msg.toLowerCase())) {
ch.writeAndFlush(new CloseWebSocketFrame());
ch.closeFuture().sync();
break;
} else if ("ping".equals(msg.toLowerCase())) {
WebSocketFrame frame = new PingWebSocketFrame(Unpooled.wrappedBuffer(new byte[] { 8, 1, 8, 1 }));
ch.writeAndFlush(frame);
} else {
WebSocketFrame frame = new TextWebSocketFrame(msg);
ch.writeAndFlush(frame);
}
}
} finally {
group.shutdownGracefully();
}
Handler:
public class WebSocketClientHandler extends SimpleChannelInboundHandler<Object> {
private final WebSocketClientHandshaker handshaker;
private ChannelPromise handshakeFuture;
public WebSocketClientHandler(WebSocketClientHandshaker handshaker) {
this.handshaker = handshaker;
}
public ChannelFuture handshakeFuture() {
return handshakeFuture;
}
#Override
public void handlerAdded(ChannelHandlerContext ctx) {
handshakeFuture = ctx.newPromise();
}
#Override
public void channelActive(ChannelHandlerContext ctx) {
handshaker.handshake(ctx.channel());
}
#Override
public void channelInactive(ChannelHandlerContext ctx) {
System.out.println("WebSocket Client disconnected!");
}
#Override
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
Channel ch = ctx.channel();
if (!handshaker.isHandshakeComplete()) {
try {
handshaker.finishHandshake(ch, (FullHttpResponse) msg);
System.out.println("WebSocket Client connected!");
handshakeFuture.setSuccess();
} catch (WebSocketHandshakeException e) {
System.out.println("WebSocket Client failed to connect");
handshakeFuture.setFailure(e);
}
return;
}
The application is able to connect to the websocket server endpoint from my local machine successfully.
But in the company datacenter where my application is deployed, I see the msg value is null and the websocket client is disconnected
Does that mean my connection is blocked at firewall? If that is the case then why did the statement "WebSocket Client connected!" is printed at all?
Thanks
The httpproxyhandler you used is correct
Just remove the BufferredReader code as mentioned below when deploying in linux, docker, etc:
Netty WebSocket Client Channel always gets inactive on Linux Server

transactional unit testing with ObjectifyService - no rollback happening

We are trying to use google cloud datastore in our project and trying to use objectify as the ORM since google recommends it. I have carefully used and tried everything i could read about and think of but somehow the transactions don't seem to work. Following is my code and setup.
#RunWith(SpringRunner.class)
#EnableAspectJAutoProxy(proxyTargetClass = true)
#ContextConfiguration(classes = { CoreTestConfiguration.class })
public class TestObjectifyTransactionAspect {
private final LocalServiceTestHelper helper = new LocalServiceTestHelper(
// Our tests assume strong consistency
new LocalDatastoreServiceTestConfig().setApplyAllHighRepJobPolicy(),
new LocalMemcacheServiceTestConfig(), new LocalTaskQueueTestConfig());
private Closeable closeableSession;
#Autowired
private DummyService dummyService;
#BeforeClass
public static void setUpBeforeClass() {
// Reset the Factory so that all translators work properly.
ObjectifyService.setFactory(new ObjectifyFactory());
}
/**
* #throws java.lang.Exception
*/
#Before
public void setUp() throws Exception {
System.setProperty("DATASTORE_EMULATOR_HOST", "localhost:8081");
ObjectifyService.register(UserEntity.class);
this.closeableSession = ObjectifyService.begin();
this.helper.setUp();
}
/**
* #throws java.lang.Exception
*/
#After
public void tearDown() throws Exception {
AsyncCacheFilter.complete();
this.closeableSession.close();
this.helper.tearDown();
}
#Test
public void testTransactionMutationRollback() {
// save initial list of users
List<UserEntity> users = new ArrayList<UserEntity>();
for (int i = 1; i <= 10; i++) {
UserEntity user = new UserEntity();
user.setAge(i);
user.setUsername("username_" + i);
users.add(user);
}
ObjectifyService.ofy().save().entities(users).now();
try {
dummyService.mutateDataWithException("username_1", 6L);
} catch (Exception e) {
e.printStackTrace();
}
List<UserEntity> users2 = this.dummyService.findAllUsers();
Assert.assertEquals("Size mismatch on rollback", users2.size(), 10);
boolean foundUserIdSix = false;
for (UserEntity userEntity : users2) {
if (userEntity.getUserId() == 1) {
Assert.assertEquals("Username update failed in transactional context rollback.", "username_1",
userEntity.getUsername());
}
if (userEntity.getUserId() == 6) {
foundUserIdSix = true;
}
}
if (!foundUserIdSix) {
Assert.fail("Deleted user with userId 6 but it is not rolledback.");
}
}
}
Since I am using spring, idea is to use an aspect with a custom annotation to weave objectify.transact around the spring service beans methods that are calling my daos.
But somehow the update due to ObjectifyService.ofy().save().entities(users).now(); is not gettign rollbacked though the exception throws causes Objectify to run its rollback code. I tried printing the ObjectifyImpl instance hashcodes and they are all same but still its not rollbacking.
Can someone help me understand what am i doing wrong? Havent tried the actual web based setup yet...if it cant pass transnational test cases there is no point in actual transaction usage in a web request scenario.
Update: Adding aspect, services, dao as well to make a complete picture. The code uses spring boot.
DAO class. Note i am not using any transactions here because as per code of com.googlecode.objectify.impl.TransactorNo.transactOnce(ObjectifyImpl<O>, Work<R>) a transnational ObjectifyImpl is flushed and committed in this method which i don't want. I want commit to happen once and rest all to join in on that transaction. Basically this is the wrong code in com.googlecode.objectify.impl.TransactorNo ..... i will try to explain my understanding a later in the question.
#Component
public class DummyDaoImpl implements DummyDao {
#Override
public List<UserEntity> loadAll() {
Query<UserEntity> query = ObjectifyService.ofy().transactionless().load().type(UserEntity.class);
return query.list();
}
#Override
public List<UserEntity> findByUserId(Long userId) {
Query<UserEntity> query = ObjectifyService.ofy().transactionless().load().type(UserEntity.class);
//query = query.filterKey(Key.create(UserEntity.class, userId));
return query.list();
}
#Override
public List<UserEntity> findByUsername(String username) {
return ObjectifyService.ofy().transactionless().load().type(UserEntity.class).filter("username", username).list();
}
#Override
public void update(UserEntity userEntity) {
ObjectifyService.ofy().save().entity(userEntity);
}
#Override
public void update(Iterable<UserEntity> userEntities) {
ObjectifyService.ofy().save().entities(userEntities);
}
#Override
public void delete(Long userId) {
ObjectifyService.ofy().delete().key(Key.create(UserEntity.class, userId));
}
}
Below is the Service class
#Service
public class DummyServiceImpl implements DummyService {
private static final Logger LOGGER = LoggerFactory.getLogger(DummyServiceImpl.class);
#Autowired
private DummyDao dummyDao;
public void saveDummydata() {
List<UserEntity> users = new ArrayList<UserEntity>();
for (int i = 1; i <= 10; i++) {
UserEntity user = new UserEntity();
user.setAge(i);
user.setUsername("username_" + i);
users.add(user);
}
this.dummyDao.update(users);
}
/* (non-Javadoc)
* #see com.bbb.core.objectify.test.services.DummyService#mutateDataWithException(java.lang.String, java.lang.Long)
*/
#Override
#ObjectifyTransactional
public void mutateDataWithException(String usernameToMutate, Long userIdToDelete) throws Exception {
//update one
LOGGER.info("Attempting to update UserEntity with username={}", "username_1");
List<UserEntity> mutatedUsersList = new ArrayList<UserEntity>();
List<UserEntity> users = dummyDao.findByUsername(usernameToMutate);
for (UserEntity userEntity : users) {
userEntity.setUsername(userEntity.getUsername() + "_updated");
mutatedUsersList.add(userEntity);
}
dummyDao.update(mutatedUsersList);
//delete another
UserEntity user = dummyDao.findByUserId(userIdToDelete).get(0);
LOGGER.info("Attempting to delete UserEntity with userId={}", user.getUserId());
dummyDao.delete(user.getUserId());
throw new RuntimeException("Dummy Exception");
}
/* (non-Javadoc)
* #see com.bbb.core.objectify.test.services.DummyService#findAllUsers()
*/
#Override
public List<UserEntity> findAllUsers() {
return dummyDao.loadAll();
}
Aspect which wraps the method annoted with ObjectifyTransactional as a transact work.
#Aspect
#Component
public class ObjectifyTransactionAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(ObjectifyTransactionAspect.class);
#Around(value = "execution(* *(..)) && #annotation(objectifyTransactional)")
public Object objectifyTransactAdvise(final ProceedingJoinPoint pjp, ObjectifyTransactional objectifyTransactional) throws Throwable {
try {
Object result = null;
Work<Object> work = new Work<Object>() {
#Override
public Object run() {
try {
return pjp.proceed();
} catch (Throwable throwable) {
throw new ObjectifyTransactionExceptionWrapper(throwable);
}
}
};
switch (objectifyTransactional.propagation()) {
case REQUIRES_NEW:
int limitTries = objectifyTransactional.limitTries();
if(limitTries <= 0) {
Exception illegalStateException = new IllegalStateException("limitTries must be more than 0.");
throw new ObjectifyTransactionExceptionWrapper(illegalStateException);
} else {
if(limitTries == Integer.MAX_VALUE) {
result = ObjectifyService.ofy().transactNew(work);
} else {
result = ObjectifyService.ofy().transactNew(limitTries, work);
}
}
break;
case NOT_SUPPORTED :
case NEVER :
case MANDATORY :
result = ObjectifyService.ofy().execute(objectifyTransactional.propagation(), work);
break;
case REQUIRED :
case SUPPORTS :
ObjectifyService.ofy().transact(work);
break;
default:
break;
}
return result;
} catch (ObjectifyTransactionExceptionWrapper e) {
String packageName = pjp.getSignature().getDeclaringTypeName();
String methodName = pjp.getSignature().getName();
LOGGER.error("An exception occured while executing [{}.{}] in a transactional context."
, packageName, methodName, e);
throw e.getCause();
} catch (Throwable ex) {
String packageName = pjp.getSignature().getDeclaringTypeName();
String methodName = pjp.getSignature().getName();
String fullyQualifiedmethodName = packageName + "." + methodName;
throw new RuntimeException("Unexpected exception while executing ["
+ fullyQualifiedmethodName + "] in a transactional context.", ex);
}
}
}
Now the problem code part that i see is as follows in com.googlecode.objectify.impl.TransactorNo:
#Override
public <R> R transact(ObjectifyImpl<O> parent, Work<R> work) {
return this.transactNew(parent, Integer.MAX_VALUE, work);
}
#Override
public <R> R transactNew(ObjectifyImpl<O> parent, int limitTries, Work<R> work) {
Preconditions.checkArgument(limitTries >= 1);
while (true) {
try {
return transactOnce(parent, work);
} catch (ConcurrentModificationException ex) {
if (--limitTries > 0) {
if (log.isLoggable(Level.WARNING))
log.warning("Optimistic concurrency failure for " + work + " (retrying): " + ex);
if (log.isLoggable(Level.FINEST))
log.log(Level.FINEST, "Details of optimistic concurrency failure", ex);
} else {
throw ex;
}
}
}
}
private <R> R transactOnce(ObjectifyImpl<O> parent, Work<R> work) {
ObjectifyImpl<O> txnOfy = startTransaction(parent);
ObjectifyService.push(txnOfy);
boolean committedSuccessfully = false;
try {
R result = work.run();
txnOfy.flush();
txnOfy.getTransaction().commit();
committedSuccessfully = true;
return result;
}
finally
{
if (txnOfy.getTransaction().isActive()) {
try {
txnOfy.getTransaction().rollback();
} catch (RuntimeException ex) {
log.log(Level.SEVERE, "Rollback failed, suppressing error", ex);
}
}
ObjectifyService.pop();
if (committedSuccessfully) {
txnOfy.getTransaction().runCommitListeners();
}
}
}
transactOnce is by code / design always using a single transaction to do things. It will either commit or rollback the transaction. there is no provision to chain transactions like a normal enterprise app would want.... service -> calls multiple dao methods in a single transaction and commits or rollbacks depending on how things look.
keeping this in mind, i removed all annotations and transact method calls in my dao methods so that they don't start an explicit transaction and the aspect in service wraps the service method in transact and ultimately in transactOnce...so basically the service method is running in a transaction and no new transaction is getting fired again. This is a very basic scenario, in actual production apps services can call other service methods and they might have the annotation on them and we could still end up in a chained transaction..but anyway...that is a different problem to solve....
I know NoSQLs dont support write consistency at table or inter table levels so am I asking too much from google cloud datastore?

Wicket 7 WebSocketBehavior

I am developing extending WebSocketBehavior in order to send logging data to a client.. have generated the logging handler and it fires as and when needed.
I am having trouble understanding how exactly to push the log entries to the clients and update the console panel. I already know the onMessage method is what I need to override with the console taking the WeSocketRequestHandler as an argument along with the message I want to send. How exactly do I get the onMessage to fire properly?? Here is the code I am using:
public class LogWebSocketBehavior extends WebSocketBehavior {
private static final long serialVersionUID = 1L;
Console console;
private Handler logHandler;
private Model model;
public LogWebSocketBehavior(Console console) {
super();
configureLogger();
this.console = console;
}
private void configureLogger() {
Logger l = Logger.getLogger(AppUtils.loggerName);
logHandler = getLoggerHandler();
l.addHandler(logHandler);
}
#Override
protected void onMessage(WebSocketRequestHandler handler, TextMessage message) {
console.info(handler, model.getObject());
}
private Handler getLoggerHandler() {
return new Handler() {
#Override
public void publish(LogRecord record) {
model.setObject(record);
}
#Override
public void flush() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void close() throws SecurityException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
};
}
private Collection<IWebSocketConnection> getConnectedClients() {
IWebSocketConnectionRegistry registry = new SimpleWebSocketConnectionRegistry();
return registry.getConnections(getApplication());
}
private void sendToAllConnectedClients(String message) {
Collection<IWebSocketConnection> wsConnections = getConnectedClients();
for (IWebSocketConnection wsConnection : wsConnections) {
if (wsConnection != null && wsConnection.isOpen()) {
try {
wsConnection.sendMessage("test");
} catch (IOException e) {
}
}
}
}
}
The logger works as I want it to, providing messages as needed, but I cannot find how to actually fire the onMessage method to update my console. Any help is appreciated...
#onMessage() is called by Wicket whenever the browser pushes a message via Wicket.WebSocket.send("some message").
It is not very clear but I guess you need to push messages from the server to the clients (the browsers). If this is the case then you need to get a handle to IWebSocketRequestHandler and use its #push(String) method. You can do this with WebSocketSettings.Holder.get(Application.get()).getConnectionRegistry().getConnection(...).push("message").
Here is the class working as I need. Thank you Martin!!
public class LogWebSocketBehavior extends WebSocketBehavior {
private static final long serialVersionUID = 1L;
Console console;
private Handler logHandler;
private IModel model;
public LogWebSocketBehavior(Console console, IModel model) {
super();
configureLogger();
this.console = console;
this.model = model;
}
private void configureLogger() {
Logger l = Logger.getLogger(AppUtils.loggerName);
logHandler = getLoggerHandler();
l.addHandler(logHandler);
}
#Override
protected void onPush(WebSocketRequestHandler handler, IWebSocketPushMessage message) {
super.onPush(handler, message);
console.info(handler, model);
}
private Handler getLoggerHandler() {
return new Handler() {
#Override
public void publish(LogRecord record) {
model.setObject(record);
sendToAllConnectedClients(record.toString());
}
#Override
public void flush() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
#Override
public void close() throws SecurityException {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
};
}
private Collection<IWebSocketConnection> getConnectedClients() {
IWebSocketConnectionRegistry registry = new SimpleWebSocketConnectionRegistry();
return registry.getConnections(getApplication());
}
private void sendToAllConnectedClients(String message) {
IWebSocketConnectionRegistry registry = new SimpleWebSocketConnectionRegistry();
WebSocketPushBroadcaster b = new WebSocketPushBroadcaster(registry);
IWebSocketPushMessage msg = new Message();
b.broadcastAll(getApplication(), msg);
}
class Message implements IWebSocketPushMessage {
public Message(){
}
}
}

GWT - Tips Needed - Check and set parameter on the session. Is this the right way?

Im made a sort of autentication on my web application using GWT. So i make these functions in the GWTServiceImpl Class :
public class PageMenuLogin extends FlowPanel {
public PageMenuLogin() {
PageMenuLogin.getService().isSetSession(new AsyncCallback<String>() {
#Override
public void onFailure(Throwable caught) {
InlineLabel err=new InlineLabel();
err.setText("Errore Di Connessione");
PageMenuLogin.this.add(err);
}
#Override
public void onSuccess(String result) {
if(result.compareTo("")==0) {
designLogin();
} else {
designLogout(result);
}
}
});
}
public final void designLogin() {
final InlineLabel menu_err=new InlineLabel("");
menu_err.setStyleName("menu_err");
this.add(menu_err);
Button menu_login_button=new Button("Login");
this.add(menu_login_button);
menu_login_button.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
getService().checkLogin("nickname", "password", new AsyncCallback<Boolean>() {
#Override
public void onFailure(Throwable caught) {
menu_err.setText("Comunicazione Fallita");
}
#Override
public void onSuccess(Boolean result) {
if (result) {
// I LOAD THE PROFILE PAGE
} else {
menu_err.setText("Username e password non validi");
}
}
});
}
});
}
}
********************************************************************************
public class GWTServiceImpl extends RemoteServiceServlet implements GWTService {
HttpServletRequest request;
HttpSession session;
public String isSetSession() {
this.request = this.getThreadLocalRequest();
this.session=this.request.getSession();
if(this.session.getAttribute("nickname")==null) {
return "";
} else {
return (String)this.session.getAttribute("nickname");
}
}
public boolean checkLogin(String username, String password) {
if("i check on the database if the user exist") {
this.session.setAttribute("nickname", value);
return true;
}
return false;
}
}
On client side, i call the GWTServiceImpl functions (server side), i check the return values and i do some operations.
Is this the right way to work with session on GWT? Any suggestion/tips/helps would be appreciated :)
Thanks for your time!!!
EDIT
New GWTServiceImpl :
public class GWTServiceImpl extends RemoteServiceServlet implements GWTService {
HttpSession session;
public String isSetSession() {
HttpSession session=getThreadLocalRequest().getSession();
if(session.getAttribute("nickname")==null) {
return "";
} else {
return (String)session.getAttribute("nickname");
}
}
public boolean checkLogin(String nickname, String password) {
HttpSession session=getThreadLocalRequest().getSession();
Database mydb=Configuration.getDatabase();
mydb.connetti();
// faccio md5 ed escape
String log_check_user=nickname;
String log_check_pass=password;
// controllo che l'utente esista
ArrayList<String[]> db_result=null;
db_result=mydb.selectQuery("SELECT nickname FROM users WHERE nickname='"+log_check_user+"' AND password='"+log_check_pass+"'");
mydb.disconnetti();
if(!db_result.isEmpty()) {
session.setAttribute("nickname", nickname);
return true;
}
return false;
}
public boolean checkLogout() {
HttpSession session=getThreadLocalRequest().getSession();
session.invalidate();
return true;
}
}
Looks like it should work. I do a lot of work with GWT, although I often forego the use of the RemoteServiceServlet in favour of passing data back and forth via JSON.
A few suggestions, though. When you're calling a method or field within the same class, you don't need to include the this keyword. It doesn't hurt, but tends to make your code longer than it needs to be. Feel free to keep it though, if you find it makes things clearer for you.
Also, unless you've got methods that actually use the request, you don't need to create the request object; you can just do
session = getThreadLocalRequest().getSession();
A final suggestion: Since you're using the session in more than one method, it might be a good idea to initialize it right away; So, instead of initializing it in isSetSession(), you could just write HttpSession session = getThreadLocalRequest().getSession();, or initialize it in the class's constructor. As it stands now, if you happen to call checkLogin() before isSetSession(), you'll get a NullPointerException since session hasn't yet been initialized.

Resources