Re: Using custom Configurator with WebSockets

2019-04-28 Thread Christopher Dodunski
Your idea of wrapping the interface returned by the registry was a stroke
of brilliance!  And I'm pleased to say it was successful.

I'm now working to resolve another small hiccup.  The IoC registry can be
configured to provide the service as either a singleton or a new instance
for each new thread.  I chose the latter.  Socket connections are
established without problem, but if Tomcat subsequently calls @OnError
and/or @OnClose on a connection, the IoC registry provides a new instance
(not good).  This suggests that the subsequent method call on the socket
comes via a new thread.  So bit of a mismatch there between sessions and
threads.

I may have to move to a singleton service, meaning no longer being able to
persist connection endpoints to a static map with a simple:
connections.put(this).

Thanks again,

Chris.


-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org



Re: Using custom Configurator with WebSockets

2019-04-26 Thread Mark Thomas
Hmm.

Looking at this some more I'm not sure this is going to work.

HarbourServerEndpoint needs to extend javax.websocket.Endpoint which it
can't do as an interface.

I think you are going to have to wrap the instance returned by the
registry. In which case you can probably go back to using POJO.

So you'd have something like (I'm typing directly in email so the
chances of this compiling first time are slim):

@ServerEndpoint(...)
HarbourServerEndpointWrapper

private HarbourServerEndpoint inner;

public HarbourServerEndpointWrapper() {
inner = RegistryProxy.getService(HarbourServerEndpoint.class);
}

@OnOpen
void onOpen(Session session, EndpointConfig config) {
inner.onOpen(Session session, EndpointConfig config);
}

@OnMessage
void onMessageMessage(Session session, Message message) {
inner.onMessageMessage(Session session, Message message);
}

@OnClose
void onClose(Session session, CloseReason reason) {
inner.onClose(Session session, CloseReason reason);
}

@OnError
void onError(Session session, Throwable throwable) {
inner.onError(Session session, Throwable throwable);
}
}

Mark

On 26/04/2019 11:07, Christopher Dodunski wrote:
> So I've converted the server endpoint from annotation-based to
> programmatic, to get around the constraint of the @ServerEndpoint
> annotation having to decorate a concrete class.  The elements of this
> annotation now occupy an implementation of ServerApplicationConfig:
> 
> 
> public class HarbourServerApplicationConfig implements
> ServerApplicationConfig {
> 
> ...
> 
> @Override
> public Set getEndpointConfigs(Set extends Endpoint>> endpointClasses) {
> Set result = new HashSet<>();
> ServerEndpointConfig configuration =
> ServerEndpointConfig.Builder.create(HarbourServerEndpoint.class,
> "/websocket/{port-name}/{username}")
> .configurator(configurator)
> .encoders(encoders)
> .decoders(decoders)
> .build();
> result.add(configuration);
> 
> return result;
> }
> }
> 
> 
> All works fine when the Builder.create() parameter is a concrete endpoint
> class, but not if an interface (the goal is having Tomcat accept as
> endpoint instances IoC proxy objects cast back to their interface).
> 
> The error message suggests that Tomcat is treating this class as concrete.
> 
> 
> 26-Apr-2019 20:00:40.605 SEVERE [ajp-nio-127.0.0.1-8009-exec-197]
> org.apache.catalina.core.ContainerBase.addChildInternal
> ContainerBase.addChild: start:
>  org.apache.catalina.LifecycleException: Failed to start component
> [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/harbour]]
> at
> org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
> at
> org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:754)
> at
> org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:730)
> at
> org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
> at
> org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:985)
> ...
> Caused by: java.lang.NullPointerException
> at
> org.apache.tomcat.websocket.pojo.PojoMethodMapping.(PojoMethodMapping.java:85)
> at
> org.apache.tomcat.websocket.server.WsServerContainer.addEndpoint(WsServerContainer.java:147)
> at org.apache.tomcat.websocket.server.WsSci.onStartup(WsSci.java:116)
> at
> org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5245)
> at
> org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
> ... 46 more
> 
> 
> Lastly, below is the interface itself (my programmatic endpoint implements
> this).
> 
> 
> public interface HarbourServerEndpoint {
> 
> void onOpen(Session session, EndpointConfig config);
> 
> void onMessageMessage(Session session, Message message);
> 
> void onClose(Session session, CloseReason reason);
> 
> void onError(Session session, Throwable throwable);
> }
> 
> 
> Unfortunately online examples of programmatic endpoints are sparse, and
> the few to be found very basic.  So am quite reliant on those familiar
> with the WebSocket library, and Tomcat's application of it.
> 
> Kind regards,
> 
> Chris.
> 
> 
> -
> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: users-h...@tomcat.apache.org
> 


-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org



Re: Using custom Configurator with WebSockets

2019-04-26 Thread Christopher Dodunski
So I've converted the server endpoint from annotation-based to
programmatic, to get around the constraint of the @ServerEndpoint
annotation having to decorate a concrete class.  The elements of this
annotation now occupy an implementation of ServerApplicationConfig:


public class HarbourServerApplicationConfig implements
ServerApplicationConfig {

...

@Override
public Set getEndpointConfigs(Set> endpointClasses) {
Set result = new HashSet<>();
ServerEndpointConfig configuration =
ServerEndpointConfig.Builder.create(HarbourServerEndpoint.class,
"/websocket/{port-name}/{username}")
.configurator(configurator)
.encoders(encoders)
.decoders(decoders)
.build();
result.add(configuration);

return result;
}
}


All works fine when the Builder.create() parameter is a concrete endpoint
class, but not if an interface (the goal is having Tomcat accept as
endpoint instances IoC proxy objects cast back to their interface).

The error message suggests that Tomcat is treating this class as concrete.


26-Apr-2019 20:00:40.605 SEVERE [ajp-nio-127.0.0.1-8009-exec-197]
org.apache.catalina.core.ContainerBase.addChildInternal
ContainerBase.addChild: start:
 org.apache.catalina.LifecycleException: Failed to start component
[StandardEngine[Catalina].StandardHost[localhost].StandardContext[/harbour]]
at
org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:167)
at
org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:754)
at
org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:730)
at
org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
at
org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:985)
...
Caused by: java.lang.NullPointerException
at
org.apache.tomcat.websocket.pojo.PojoMethodMapping.(PojoMethodMapping.java:85)
at
org.apache.tomcat.websocket.server.WsServerContainer.addEndpoint(WsServerContainer.java:147)
at org.apache.tomcat.websocket.server.WsSci.onStartup(WsSci.java:116)
at
org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5245)
at
org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 46 more


Lastly, below is the interface itself (my programmatic endpoint implements
this).


public interface HarbourServerEndpoint {

void onOpen(Session session, EndpointConfig config);

void onMessageMessage(Session session, Message message);

void onClose(Session session, CloseReason reason);

void onError(Session session, Throwable throwable);
}


Unfortunately online examples of programmatic endpoints are sparse, and
the few to be found very basic.  So am quite reliant on those familiar
with the WebSocket library, and Tomcat's application of it.

Kind regards,

Chris.


-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org



Re: Using custom Configurator with WebSockets

2019-04-24 Thread Christopher Dodunski
Thanks Mark, you've made it clear that annotating the interface is not an
option.

Converting my server endpoint from annotation based to programmatic is not
a problem, nor is implementing ServerApplicationConfig to configure what
were previously @ServerEndpoint elements: value, encoders, decoders,
configurator.

What is not clear is how this will solve the problem of Tomcat not
accepting an interface as an endpoint.  Afterall, the programmatic
approach still employs Configurator.getEndpointInstance(), and this is
where I came unstuck with annotations.

Below is the beginnings of my custom ServerApplicationConfig implementation.



public class CustomServerAppConfig implements ServerApplicationConfig {

@Override
public Set getEndpointConfigs(Set> endpointClasses) {
Set result = new HashSet<>();
for (Class epClass : endpointClasses) {
if (epClass.equals(MyEndpointInterface.class)) {
ServerEndpointConfig sec =
ServerEndpointConfig.Builder.create(epClass,
"/websocket").build();
result.add(sec);
}
}
return result;
}

@Override
public Set> getAnnotatedEndpointClasses(Set> scanned) {
return Collections.emptySet();
}
}


Regards,

Chris.


-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org



Re: Using custom Configurator with WebSockets

2019-04-24 Thread Mark Thomas
On 24/04/2019 07:16, Christopher Dodunski wrote:
> Hi Mark,
> 
> Looking at the Tapestry-IoC Registry code I notice that although it
> constructs a (plastic) service proxy object, it does cast it to its
> associated interface before making it available from the registry's
> getService() method.  So if I move the WebSocket annotations to my
> interface as previously thought, Tomcat should be getting back from the
> registry what it expects: an instance of the interface annotated with
> @ServerEndpoint.
> 
> Just wondering how this sits with your understanding of the WebSocket
> library.

It won't work. From the Java WebSockets specification:

4.1
@ServerEndpoint
This class level annotation signifies that the Java class it decorates
must be deployed by the implementation as a websocket server endpoint
and made available in the URI-space of the websocket implementation.
*The class must be public, concrete, and have a public no-args constructor.*

(my emphasis)

I don't see a way to do this with annotations. You are going to need to
do it programmatically.

The WebSocket examples that ship with Tomcat include several endpoints
configured this way. There are further examples in the unit tests.

Mark

-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org



Re: Using custom Configurator with WebSockets

2019-04-23 Thread Christopher Dodunski
Hi Mark,

Looking at the Tapestry-IoC Registry code I notice that although it
constructs a (plastic) service proxy object, it does cast it to its
associated interface before making it available from the registry's
getService() method.  So if I move the WebSocket annotations to my
interface as previously thought, Tomcat should be getting back from the
registry what it expects: an instance of the interface annotated with
@ServerEndpoint.

Just wondering how this sits with your understanding of the WebSocket
library.

Regards,

Chris.


-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org



Re: Using custom Configurator with WebSockets

2019-04-23 Thread Christopher Dodunski
Based on what you wrote regarding WebSocket annotations not following Java
inheritance, I imagine the below wouldn't work either.


public class MyServerEndpointConfig extends ServerEndpointConfig {

@Override
public Class getEndpointClass() {
return MyServiceInterface.class;
}
}




-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org



Re: Using custom Configurator with WebSockets

2019-04-23 Thread Christopher Dodunski
> The custom Configurator looks fine. The problem is with trying to do
> this with a POJO endpoint. There is an underlying assumption that - for
> a POJO endpoint - the endpoints will will instances of the POJO class.
> This doesn't seem to hold in your case so hence it breaks.
>
> The WebSocket spec explicitly states that WebSocket annotations do not
> follow Java inheritance so moving the annotation to the interface is not
> an option.
>
> I think you are going to have to build your ServerEndpointConfig
> programmatically so you can specify the correct endpoint class.
>
> Mark

Thank you very much Mark for explaining.  After examining the Tapestry-IoC
a little closer, I discovered that what is being returned from the
Registry is in fact a 'service proxy object'.  It implements the same
service interface as my implementation class, but it is NOT an instance of
my implementation class.

I was thinking to shift my WebSocket annotations from the service
implementation to the service interface, but you write that this won't
work either.

So I searched the net and found a number of examples of using build() to
define an endpoint configuration.  Do you happen to have an example where
the objective was the same - or very similar to - my own?  I guess the
goal is in having Tomcat accept a service proxy object that merely
implements a given service interface, yes?

Quoting from the Tapestry-IoC documentation:

"Services consist of two main parts: a service interface and a service
implementation. The service interface is how the service will be
represented throughout the rest of the registry. Since what gets passed
around is normally a proxy, you can't expect to cast a service object down
to the implementation class (you'll see a ClassCastException instead). In
other words, you should be careful to ensure that your service interface
is complete, since Tapestry IoC effectively walls you off from back doors
such as casts."

Regards,

Chris.


-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org



Re: Using custom Configurator with WebSockets

2019-04-22 Thread Mark Thomas
On 17/04/2019 22:58, Christopher Dodunski wrote:
> Hello,
> 
> Just a quick question with regard to extending
> ServerEndpointConfig.Configurator to override Tomcat's default action of
> instantiating the POJO class annotated with @ServerEndpoint on receiving a
> WebSocket request.  My reason for doing this is that my endpoint class
> depends on IoC dependency injection, and therefore needs to be got from
> the registry to have its dependencies in place.
> 
> My Configurator method:
> 
> 
> @Override
> public  T getEndpointInstance(Class endpointClass) throws
> InstantiationException {
> return
> endpointClass.cast(RegistryProxy.getService(HarbourServerEndpoint.class));
> }
> 
> 
> The @ServerEndpoint annotation is placed on on my
> HarbourServerEndpointImpl POJO class, not the interface that it
> implements.  Based on the below runtime catalina.out error message the
> problem appears to be that the registry is returning HarbourServerEndpoint
> whereas Tomcat is expecting an instance of HarbourServerEndpointImpl?
> 
> I'm hoping someone can please explain what is going wrong with my custom
> Configurator.

The custom Configurator looks fine. The problem is with trying to do
this with a POJO endpoint. There is an underlying assumption that - for
a POJO endpoint - the endpoints will will instances of the POJO class.
This doesn't seem to hold in your case so hence it breaks.

The WebSocket spec explicitly states that WebSocket annotations do not
follow Java inheritance so moving the annotation to the interface is not
an option.

I think you are going to have to build your ServerEndpointConfig
programmatically so you can specify the correct endpoint class.

Mark


> 
> 
> 15-Apr-2019 12:45:28.488 SEVERE [http-nio-8080-exec-915]
> org.apache.coyote.AbstractProtocol$ConnectionHandler.process Error reading
> request, ignored
>  java.lang.ClassCastException: Cannot cast
> $HarbourServerEndpoint_39c9cc24eb8b2a to
> com.optomus.harbour.services.HarbourServerEndpointImpl
> at java.lang.Class.cast(Class.java:3369)
> at
> com.optomus.harbour.services.HarbourServerEndpointConfigurator.getEndpointInstance(HarbourServerEndpointConfigurator.java:17)
> at
> org.apache.tomcat.websocket.pojo.PojoEndpointServer.onOpen(PojoEndpointServer.java:44)
> at
> org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.init(WsHttpUpgradeHandler.java:133)
> at
> org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:846)
> at
> org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471)
> at
> org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
> at
> java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
> at
> java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
> at
> org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
> at java.lang.Thread.run(Thread.java:748)
> 
> 
> Finally, with no casting at all, the compiler gives the error:
> 
> Error:(17, 40) java: incompatible types: inference variable T has
> incompatible bounds
> equality constraints:
> com.optomus.harbour.services.HarbourServerEndpoint
> upper bounds: T,java.lang.Object
> 
> 
> -
> To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: users-h...@tomcat.apache.org
> 


-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org



Using custom Configurator with WebSockets

2019-04-17 Thread Christopher Dodunski
Hello,

Just a quick question with regard to extending
ServerEndpointConfig.Configurator to override Tomcat's default action of
instantiating the POJO class annotated with @ServerEndpoint on receiving a
WebSocket request.  My reason for doing this is that my endpoint class
depends on IoC dependency injection, and therefore needs to be got from
the registry to have its dependencies in place.

My Configurator method:


@Override
public  T getEndpointInstance(Class endpointClass) throws
InstantiationException {
return
endpointClass.cast(RegistryProxy.getService(HarbourServerEndpoint.class));
}


The @ServerEndpoint annotation is placed on on my
HarbourServerEndpointImpl POJO class, not the interface that it
implements.  Based on the below runtime catalina.out error message the
problem appears to be that the registry is returning HarbourServerEndpoint
whereas Tomcat is expecting an instance of HarbourServerEndpointImpl?

I'm hoping someone can please explain what is going wrong with my custom
Configurator.


15-Apr-2019 12:45:28.488 SEVERE [http-nio-8080-exec-915]
org.apache.coyote.AbstractProtocol$ConnectionHandler.process Error reading
request, ignored
 java.lang.ClassCastException: Cannot cast
$HarbourServerEndpoint_39c9cc24eb8b2a to
com.optomus.harbour.services.HarbourServerEndpointImpl
at java.lang.Class.cast(Class.java:3369)
at
com.optomus.harbour.services.HarbourServerEndpointConfigurator.getEndpointInstance(HarbourServerEndpointConfigurator.java:17)
at
org.apache.tomcat.websocket.pojo.PojoEndpointServer.onOpen(PojoEndpointServer.java:44)
at
org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.init(WsHttpUpgradeHandler.java:133)
at
org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:846)
at
org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1471)
at
org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)


Finally, with no casting at all, the compiler gives the error:

Error:(17, 40) java: incompatible types: inference variable T has
incompatible bounds
equality constraints:
com.optomus.harbour.services.HarbourServerEndpoint
upper bounds: T,java.lang.Object


-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org