I am trying to get a normal AbstractHandler and WebSocketAdapter to work at the same time.
jetty-version: jetty-9.4.8.v20171121,
I have a simple class Foo that extends org.eclipse.jetty.server.handler.AbstractHandler.
Also a class Bar that extends org.eclipse.jetty.websocket.api.WebSocketAdapter
Glue-class:
#SuppressWarnings("serial")
public class Glue extends WebSocketServlet {
#Override
public void configure(WebSocketServletFactory factory) {
factory.register(Bar.class);
}
}
Now I try to make a server that uses both of these:
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
context.setHandler(new Foo());
ServletHolder holder = new ServletHolder("ws-events", Glue.class);
context.addServlet(holder, "/whatever/*");
Server server = new Server(80);
server.setHandler(context);
server.start();
This starts and when I go to localhost I see the content that Foo should display, but I can't connect to the websocket. It looks like all requests go to Foo.
When I remove the context.setHandler(new Foo()); line it obviously does not display the html-content anymore, but I can connect to the websocket.
I want both to work at the same time.
Don't mix Handlers for responses and ServletContextHandler.
Change Foo to be a Servlet, assign it a reasonable url-pattern as a Servlet.
The reason is because your Foo executes before any of the servlet code.
The only other way to accomplish this with a Handler is to make your Foo handler websocket and HTTP/1.1 upgrade aware and not execute when the request is detected to be for WebSocket upgrade. (This is a bad idea! don't do it! its not backward compatible when WebSocket over HTTP/2 arrives!)
Also note, you can have your Glue class implement doGet() and service HTML if a non-websocket client asks for HTTP content on the same url-pattern.
BTW, If you want static html to be served, don't do that in your own code. Assign a reasonable "Base Resource Location" to your ServletContextHandler and add a DefaultServlet to serve the static resources that don't match another url-pattern.
See prior answer on this: https://stackoverflow.com/a/20223103/775715
Related
I have the below method which uses websocket connection to send request,
public void sendLoginRequest(){
WebSocket webSocket = webSocketConnection.getSocketConnection();
Thread.sleep(300 * 1000);
JSONObject loginJson = new JSONObject();
loginJson.put("username","test");
webSocket.sendText(loginJson.toString());
}
I have created my tested case with,
#Test
public void testSendLoginRequest() {
obj.sendLoginRequest();
}
Due to Thread.sleep, it's waiting for 5 min. I'm just curious to control this flow, and make sure the data send to websocket. As far as i know verify method invokes on Mock object, but in this case how can i achieve it.
P.S: I use com.refinitiv.qc.ert.infrastructure.socket.WebSocketConnection API.
I am using embedded Jetty v9.4.x and have the following issue:
My server registers a ServletContextListener:
final WebAppContext context = new WebAppContext();
// add listener
context.addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener() {
#Override
public void lifeCycleStarting(LifeCycle event) {
ContextHandler.Context ctx = context.getServletContext();
ctx.setExtendedListenerTypes(true);
ctx.addListener("LISTENER_CLASS_NAME");
}
});
My listener gets called on Servet start. However, my context listener registers a ServletRequestListener inside:
servletContext.addListener(foo.MyServletRequestListener.class);
And this fails with the following exception:
java.lang.UnsupportedOperationException
at org.eclipse.jetty.servlet.ServletContextHandler$Context.addListener(ServletContextHandler.java:1506)
And when I looked it seems that context is not enabled (at least, this flags makes an exception to be thrown).
When I run the same application with the web.xml everything works.
How can I let the contextListener register a ServletRequestListener?
edit
There is explict note in Jetty code:
//toggle state of the dynamic API so that the listener cannot use it
This is enabled only on programatically added listeners - using API and not web-xml.
How I can make this work???
There are many different kinds of listeners in Jetty, each with their own specific set of add/remove/get/set methods.
Your AbstractLifeCycleListener is a Jetty LifeCycle listener, applying specifically for the Jetty internal starting/started/stopping/stopped of the various beans within Jetty.
Your implementation of this listener in your question is incomplete and shows a lack of understanding of the LifeCycleEvent (you are not looking for a specific bean to be started), your implementation will run hundreds of times. (once for each bean being started).
The use of ServletContext.addListener() has rules around it, and those specify that it can only be used during the ServletContext initialization phase (not before, not after). The use of ServletContext.addListener() outside of this phase is supposed to throw an IllegalStateException (the javadoc even says so)
The ServletContext.addListener() also has a limited set of servlet Listeners that are allowed to be used with it, far less then the number of listeners types that are valid with a Web App, or can be declared within a WEB-INF/web.xml, or flagged with the #WebListener annotation.
The only way to use the ServletContext.addListener() is from within the webapp itself, using webapp code, from within the webapp's own classloader.
The places to use ServletContext.addListener() are ...
ServletContainerInitializer.onStartup()
ServletContextListener.contextInitialized()
Filter.init()
Servlet.init()
As you can see, all of these locations are defined from within the webapp itself.
The existence of ServletContextHandler.addEventListener(EventListener) is an embedded-jetty work around, which allows the Listener to be added on construction of the ServletContextHandler, but not called until the actual event occurs.
The use of ServletContextHandler.addEventListener(EventListener) is equivalent to using the WEB-INF/web.xml to declare the Listener you are interested in having be used.
Example:
package jetty.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
public class ServletContextListenerExample
{
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
MyContextListener contextListener = new MyContextListener();
context.addEventListener(contextListener);
// for context based static file serving and error handling
context.addServlet(DefaultServlet.class, "/");
HandlerList handlers = new HandlerList();
handlers.addHandler(context);
// for non-context error handling
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
server.start();
server.join();
}
public static class MyContextListener implements ServletContextListener
{
#Override
public void contextInitialized(ServletContextEvent sce)
{
System.err.printf("MyContextListener.contextInitialized(%s)%n", sce);
sce.getServletContext().addListener(new MyRequestListener());
}
#Override
public void contextDestroyed(ServletContextEvent sce)
{
System.err.printf("MyContextListener.contextDestroyed(%s)%n", sce);
}
}
public static class MyRequestListener implements ServletRequestListener
{
#Override
public void requestDestroyed(ServletRequestEvent sre)
{
System.err.printf("MyRequestListener.requestDestroyed(%s)%n", sre);
}
#Override
public void requestInitialized(ServletRequestEvent sre)
{
System.err.printf("MyRequestListener.requestInitialized(%s)%n", sre);
}
}
}
This will register MyContextListener which implements both javax.servlet.ServletContextListener.
When the ServletContext initialization phase kicks in, the contextInitialized() event is triggered.
The implementation of contextInitalized() then uses the passed in ServletContext to add a new MyRequestListener (which implements javax.servlet.ServletRequestListener) via the ServletContext.addListener() API.
Output of the above, and hitting http://localhost:8080/ from a browser ...
2018-06-28 09:42:06.352:INFO::main: Logging initialized #340ms to org.eclipse.jetty.util.log.StdErrLog
2018-06-28 09:42:06.475:INFO:oejs.Server:main: jetty-9.4.11.v20180605; built: 2018-06-05T18:24:03.829Z; git: d5fc0523cfa96bfebfbda19606cad384d772f04c; jvm 9.0.4+11
MyContextListener.contextInitialized(javax.servlet.ServletContextEvent[source=ServletContext#o.e.j.s.ServletContextHandler#12e61fe6{/,null,STARTING}])
2018-06-28 09:42:06.532:INFO:oejsh.ContextHandler:main: Started o.e.j.s.ServletContextHandler#12e61fe6{/,null,AVAILABLE}
2018-06-28 09:42:06.695:INFO:oejs.AbstractConnector:main: Started ServerConnector#4567f35d{HTTP/1.1,[http/1.1]}{0.0.0.0:8080}
2018-06-28 09:42:06.695:INFO:oejs.Server:main: Started #690ms
MyRequestListener.requestInitialized(javax.servlet.ServletRequestEvent[source=ServletContext#o.e.j.s.ServletContextHandler#12e61fe6{/,null,AVAILABLE}])
MyRequestListener.requestDestroyed(javax.servlet.ServletRequestEvent[source=ServletContext#o.e.j.s.ServletContextHandler#12e61fe6{/,null,AVAILABLE}])
Caution: Be aware that there are many more listener APIs and listener types on Jetty then discussed here, they exist for other features / components with Jetty that are unrelated to your question.
Don't get hung up on them, skip them, ignore them and you'll be fine.
Looking at the Bond Comm documentation, it wasn't clear to me how the functions I define for services are connected to specific functions in my code.
Does it look for a function with the same signature in the project and assign it to the endpoint? Is there some underlying settings file I am missing?
NB: Bond Comm is deprecated. It isn't supported any more, and will be removed from Bond in an upcoming release. Bond-over-gRPC is its replacement.
When using either Bond-over-gRPC or Bond Comm, the generated server-side code is an abstract class with an abstract method for each method in the service definition. To provide your logic for these methods, you inherit from the generated base and provide implementations for all the service methods. Then, typically in your main function, you create a Server (for Bond-over-gRPC) or a Listener (for Bond Comm) and register an instance of the implementation class. This sets up the routing for IDL service method to your implementation code.
From the Bond-over-gRPC C# documentation:
Given a service definition like the following:
service Example
{
ExampleResponse ExampleMethod(ExampleRequest);
}
gbc will generate C# classes for gRPC with the --grpc flag:
gbc c# --grpc example.bond
...
To build the service functionality, simply write a concrete service
implementation by subclassing the server base and supplying the
business logic:
public class ExampleServiceImpl : Example.ExampleBase {
public override async Task<IMessage<ExampleResponse>>
ExampleMethod(
IMessage<ExampleRequest> param,
ServerCallContext context)
{
ExampleRequest request = param.Payload.Deserialize();
var response = new ExampleResponse();
// Service business logic goes here
return Message.From(response);
}
}
This service implementation is hooked up to a gRPC server as follows:
var server = new Grpc.Core.Server {
Services = { Example.BindService(new ExampleServiceImpl()) },
Ports = { new Grpc.Core.ServerPort(ExampleHost, ExamplePort, Grpc.Core.ServerCredentials.Insecure) } };
server.Start();
At this point the server is ready to receive requests and route them to the
service implementation.
There are more examples as well:
a standalone C# project
a C# ping/pong example
a C++ "Hello World" example
a C++ ping/pong example
It's worth pointing out that (Bond-over-) gRPC and Bond Comm are neither SOAP nor REST. The question was tagged with web-service, and sometimes people mean SOAP/REST when they talk about web services. I think of both gRPC and Bond Comm as custom binary protocols over TCP, although gRPC is run atop HTTP/2.
Trying to write some proper AEM integration tests using the aem-mocks framework. The goal is to try and test a servlet by calling its path,
E.g. an AEM servlet
#SlingServlet(
paths = {"/bin/utils/emailSignUp"},
methods = {"POST"},
selectors = {"form"}
)
public class EmailSignUpFormServlet extends SlingAllMethodsServlet {
#Reference
SubmissionAgent submissionAgent;
#Reference
XSSFilter xssFilter;
public EmailSignUpFormServlet(){
}
public EmailSignUpFormServlet(SubmissionAgent submissionAgent, XSSFilter xssFilter) {
this.submissionAgent = submissionAgent;
this.xssFilter = xssFilter;
}
#Override
public void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
String email = request.getParameter("email");
submissionAgent.saveForm(xssFilter.filter(email));
}
}
Here is the corresponding test to try and do the integration testing. Notice how I've called the servlet's 'doPost' method, instead of 'POST'ing via some API.
public class EmailSignUpFormServletTest {
#Rule
public final AemContext context = new AemContext();
#Mock
SubmissionAgent submissionAgent;
#Mock
XSSFilter xssFilter;
private EmailSignUpFormServlet emailSignUpFormServlet;
#Before
public void setup(){
MockitoAnnotations.initMocks(this);
Map<String,String> report = new HashMap<>();
report.put("statusCode","302");
when(submissionAgent.saveForm(any(String.class)).thenReturn(report);
}
#Test
public void emailSignUpFormDoesNotRequireRecaptchaChallenge() throws IOException {
// Setup test email value
context.request().setQueryString("email=test.only#mail.com");
//===================================================================
/*
* WHAT I END UP DOING:
*/
// instantiate a new class of the servlet
emailSignUpFormServlet = new EmailSignUpFormServlet(submissionAgent, xssFilter);
// call the post method (Simulate the POST call)
emailSignUpFormServlet.doPost(context.request(),context.response());
/*
* WHAT I WOULD LIKE TO DO:
*/
// send request using some API that allows me to do post to the framework
// Example:
// context.request().POST("/bin/utils/emailSignUp") <--- doesn't exist!
//===================================================================
// assert response is internally redirected, hence expected status is a 302
assertEquals(302,context.response().getStatus());
}
}
I've done a lot of research on how this could be done (here) and (here), and these links show a lot about how you can set various parameters for context.request() object. However, they just don't show how to finally execute the 'post' call.
What you are trying to do is mix a UT with IT so this won't be easy at least with the aem-mocks framework. Let me explain why.
Assuming that you are able to call your required code
/*
* WHAT I WOULD LIKE TO DO:
*/
// send request using some API that allows me to do post to the framework
// Example:
// context.request().POST("/bin/utils/emailSignUp") <--- doesn't exist!
//===================================================================
Your test will end up executing all the logic in SlingAllMethodsServlet class and its parent classes. I am assuming that this is not what you want to test as these classes are not part of your logic and they already have other UT/IT (under respective Apache projects) to cater for testing requirements.
Also, looking at your code, bulk of your core logic resides in following snipper
String email = request.getParameter("email");
submissionAgent.saveForm(xssFilter.filter(email));
Your UT criteria is already met by the following line of your code:
emailSignUpFormServlet.doPost(context.request(),context.response());
as it covers most of that logic.
Now, if you are looking for proper IT for posting the parameters and parsing them all the way down to doPost method then aem-mocks is not the framework for that because it does not provide it in a simple way.
You can, in theory, mock all the layers from resource resolver, resource provider and sling servlet executors to pass the parameters all the way to your core logic. This can work but it won't benefit your cause because:
Most of the code is already tested via other UT
Too many internal mocking dependencies might make the tests flaky or version dependant.
If you really want to do pure IT, then it will be easier to host the servlet in an instance and access it via HttpClient. This will ensure that all the layers are hit. A lot of tests are done this way but it feels a bit heavy handed for the functionality you want to test and there are better ways of doing it.
Also the reason why context.request().POST doesn't exist is because context.request() for is a mocked state for the sake of testing. You want to actually bind and mock Http.Post operations which needs some way to resolve to your servlet and that is not supported by the framework.
Hope this helps.
i am using spring cloud's eureka and feign to communicate between some services (lets say A and B). Now id like to unittest my service layer of a single service (A). The problem is, that this service (A) is using a feign client to request some information of the other service (B).
Running the unittests without any special configuration throws the following exception: java.lang.RuntimeException: com.netflix.client.ClientException: Load balancer does not have available server for client: service-b => but i do not want any server to run.
My question is: Is there a way to mock the feign client, so i can unittest my service (A) without running an eureka instance and service (B)?
Edit:
I ended up creating a stub for the feign client. The stub is marked as a primary component to force spring instantiating the stub within my tests.
This is the solution i came up with.
//the feign client
#FeignClient("user")
public interface UserClient {
UserEntity getUser();
}
//the implementation i use for the tests
#Component
#Primary //mark as primary implementation
public class UserClientTestImpl implements UserClient {
#Override public UserEntity getUser() {
return someKindOfUser;
}
}
The question is ... do you even need to mock? I often see that people mention "mock" as the first solution to anything that "should not be part of the unit test". Mocking is a technique, not the solution to everything. (see here).
If you are still at the early stages of your code, just refactor and use something else instead of depending on the concrete instance of the Feign Client. You might use an interface, an abstract class, a trait or whatever you want. Don't depend on the object itself, otherwise you have to "mock it".
public interface IWebClient {
public String get(...);
public String post(...);
}
To the question: but I will have other code that will do exactly the same (except that it will be on the concrete instance of Feign), what do I do then?
Well, you can write a functional test and call an instance of a web server that you can setup locally - or use Wiremock, as mentioned by Marcin Grzejszczak in one of the answers.
public class FeignClientWrapper implements IWebClient {
private feign = something
public String get() {
feign.get( ... )
}
public String post() {
feign.post( ... )
}
}
Unit tests are used to test algorithms, if/else, loops: how units work. Don't write code to make mocks fit - it must be the other way around: your code should have less dependencies, and you should mock only when you need to verify the behavior (otherwise you can use a stub or a fake object): do you need to verify the behavior? Do you need to test that a particular method gets called in your code? Or that a particular method gets called with X, Y, and Z for 3 times in a row? Well, then yes, mocking is ok.
Otherwise, use a fake object: what you want is to test just the call/response and maybe the status code. All you probably want is to test how your code reacts to different outputs (e.g., the field "error" is present or not in a JSON response), different status codes (assuming that the Client documentation is right: 200 OK when GET, 201 when POST, etc).
Mocking a feign client is really useful in microservice component tests. You want to test one microservice without having to start all the other microservices.
If you're using Spring (and it looks like you are), the #MockBean annotation together with a bit of Mockito code will do the job.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment =
SpringBootTest.WebEnvironment.DEFINED_PORT)
public class TestYourComponent {
#Configuration
#Import({YourConfiguration.class})
public static class TestConfiguration {
}
#MockBean
private UserClient userClient;
#Test
public void someTest()
{
//...
mockSomeBehavior();
//...
}
private void mockSomeBehavior() {
Mockito.doReturn(someKindOfUser).when(userClient).getUser();
}
}
If you need to use a mock you can use Wiremock to stub the response for a given request - http://wiremock.org/stubbing.html. That way you will do integration tests with real HTTP requests sent. For unit testing the answer from #Markon is very good.