For (JUnit) testing purposes I'd like to make a simple application that would be the server to be invoked using Spring HttpInvoker. I don't want to make a real webapp to be deployed in any servlet container, only something standalone.
Do you have any ideas how to make it as simply as possible? (Solutions without embedded Tomcat or stuff are preferred..)
This will work out well for you - http://docs.codehaus.org/display/JETTY/ServletTester
#BeforeClass
public static void initServletContainer() throws Exception {
tester = new ServletTester();
tester.setContextPath("/");
tester.addServlet(DummyServlet.class, "/dummy");
baseUrl = tester.createSocketConnector(true);
tester.start();
System.out.println(baseUrl);
}
You can start up the server in your #BeforeClass method, record the baseUrl where the server starts up and use this url to test your client.
http://code.google.com/p/jianwikis/wiki/SpringHttpRemotingWithEmbeddedJettyServer
Related
First time ever using tomcat/setting up a webapp from scratch so please be mercyful. I have created an embedded tomcat server which basically looks like this:
public class Server implements Runnable {
private Tomcat tomcat;
public Server() {
tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.addWebapp("", new File("src/webapp").getAbsolutePath());
}
#Override
public void run() {
try {
tomcat.start();
tomcat.getServer().await();
} catch (LifecycleException e) {
e.printStackTrace();
}
}
And I run it in a main that looks like this:
public static void main(String[] args) throws Exception {
Thread thread = new Thread(server);
thread.start();
Foo foo = new Foo();
(thread.isAlive()) {
foo.doStuff();
TimeUnit.HOURS.sleep(interval);
}
}
The purpose of the program is to run the http-server on one thread while the class Foo does some stuff in the backend once every so-and-so hours. Probably not the correct way to create a webapp, but it's the best I've managed.
However, now that I'm trying to package it I'm running into issues because once it is packaged (using Maven) the Server doesn't seem to be able to find the webapp-folder. After a couple hours of googling and trying out a a lot of stuff involving war:s and jar:s I've come to the conclusion that there is something about this embedded tomcat stuff that I'm not understanding.
So, my questions are:
Is the way I've implemented my webapp correct? I'm getting the feeling that it's not but I can't really confirm it.
2a. If incorrect, how does one do it correctly?
2b. If correct, how does one package it into a runnable jar/war?
This is a little bit of a non-standard way of going about it. Rather than writing all your own application logic to handle an integrated web server, would you not consider leveraging something that's already there? You can create a Java project in Spring boot which contains its own embedded web server.
There's a sample starter example here that should get you going - https://spring.io/guides/gs/serving-web-content/
I would recommend this approach rather than writing it yourself as Spring Boot is an industry standard that is widely used, proven, and tested.
I have several instances of my spring boot app, which in parallel do some work with DB. Each instance is running in separate JVM.
Is it a way to write a test in Java for testing that on one JVM? Like following:
Setup some embedded DB for testing purposes or even just mock it.
Start 2-5 instances of my Spring boot app
Wait some time
Stop all started instances
Verify DB and check that all the conditions are met.
Each instance has its own context and classpath.
I think that I could achieve that with some shell script scenario but I'd like to make it in Java.
What would be the best approach here?
You can run them multiple times using different ports.
I did something similar
#RunWith(SpringJUnit4ClassRunner.class)
public class ServicesIntegrationTest {
private RestTemplate restTemplate = new RestTemplate();
#Test
public void runTest() throws Exception {
SpringApplicationBuilder uws = new SpringApplicationBuilder(UserWebApplication.class)
.properties("server.port=8081",
"server.contextPath=/UserService",
"SOA.ControllerFactory.enforceProxyCreation=true");
uws.run();
SpringApplicationBuilder pws = new SpringApplicationBuilder(ProjectWebApplication.class)
.properties("server.port=8082",
"server.contextPath=/ProjectService",
"SOA.ControllerFactory.enforceProxyCreation=true");
pws.run();
String url = "http://localhost:8081/UserService/users";
ResponseEntity<SimplePage<UserDTO>> response = restTemplate.exchange(
url,
HttpMethod.GET,
null,
new ParameterizedTypeReference<SimplePage<UserDTO>>() {
});
here the source.
I start the server from the terminal via the java -jar target/test-service-1.0-jar-with-dependencies.jar command
However when running tests from Intellij Idea, I just cannot figure out how to start the server..
This is the current code, which doesn't work
private HttpServer server;
private WebTarget target;
#Before
public void setUp() throws Exception {
// start the server
server = Main.startServer();
// create the client
Client c = ClientBuilder.newBuilder().register(JacksonFeature.class).build();
// uncomment the following line if you want to enable
// support for JSON in the client (you also have to uncomment
// dependency on jersey-media-json module in pom.xml and Main.startServer())
// --
// c.configuration().enable(new org.glassfish.jersey.media.json.JsonJaxbFeature());
target = c.target(Main.BASE_URI);
}
This is my startServer code
public static HttpServer startServer() {
// create a resource config that scans for JAX-RS resources and providers
ResourceConfig rc = new ResourceConfig().packages("com.test.service").register(JacksonFeature.class);
EncodingFilter.enableFor(rc, GZipEncoder.class);
rc.register(LoggingFilter.class);
rc.register(MultiPartFeature.class);
rc.register(CORSResponseFilter.class);
// rc.property("config", configParams);
// create and start a new instance of grizzly http server
// exposing the Jersey application at BASE_URI
HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
//httpServer.getServerConfiguration().addHttpHandler(shh);
return httpServer;
}
It should just start by itself. But for testing you probably want to control when it starts. You can pass false as the third argument to the server factory method. That way you control when it should start.
You can call start and stop on the HttpServer instance in your before and after methods in your test. You'll need to update the code in the Main class also, to call start().
You might also want to check out Jersey Test Framework. Here you won't need to start and stop any servers. The framework will handle it for you. It also makes your tests more configurable than your current set up. Say you only want one resource registered or you want to inject some mocks services. Personally, I would go with the test framework.
I have the below route. In unit test, since I doesn't have the FTP server available, I'd like to use camel's test support and send an invalid message to "ftp://hostname/input" and verify that it failed and routed to "ftp://hostname/error".
I gone through the documentation which mainly talks about using the "mock:" endpoint but I am not sure how to use it in this scenario.
public class MyRoute extends RouteBuilder
{
#Override
public void configure()
{
onException(EdiOrderParsingException.class).handled(true).to("ftp://hostname/error");
from("ftp://hostname/input")
.bean(new OrderEdiTocXml())
.convertBodyTo(String.class)
.convertBodyTo(Document.class)
.choice()
.when(xpath("/cXML/Response/Status/#text='OK'"))
.to("ftp://hostname/valid").otherwise()
.to("ftp://hostname/invalid");
}
}
As Ben says you can either setup a FTP server and use the real components. The FTP server can be embedded, or you can setup a FTP server in-house. The latter is more like an integration testing, where you may have a dedicated test environment.
Camel is very flexible in its test kit, and if you want to build an unit test that do not use the real FTP component, then you can replace that before the test. For example in your example you can replace the input endpoint of a route to a direct endpoint to make it easier to send a message to the route. Then you can use an interceptor to intercept the sending to the ftp endpoints, and detour the message.
The advice with part of the test kit offers these capabilities: http://camel.apache.org/advicewith.html. And is also discussed in chapter 6 of the Camel in action book, such as section 6.3, that talks about simulating errors.
In your example you could do something a like
public void testSendError() throws Exception {
// first advice the route to replace the input, and catch sending to FTP servers
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith("direct:input");
// intercept valid messages
interceptSendToEndpoint("ftp://hostname/valid")
.skipSendToOriginalEndpoint()
.to("mock:valid");
// intercept invalid messages
interceptSendToEndpoint("ftp://hostname/invalid")
.skipSendToOriginalEndpoint()
.to("mock:invalid");
}
});
// we must manually start when we are done with all the advice with
context.start();
// setup expectations on the mocks
getMockEndpoint("mock:invalid").expectedMessageCount(1);
getMockEndpoint("mock:valid").expectedMessageCount(0);
// send the invalid message to the route
template.sendBody("direct:input", "Some invalid content here");
// assert that the test was okay
assertMockEndpointsSatisfied();
}
From Camel 2.10 onwards we will make the intercept and mock a bit easier when using advice with. As well we are introducing a stub component. http://camel.apache.org/stub
Have a look at MockFtPServer!
<dependency>
<groupId>org.mockftpserver</groupId>
<artifactId>MockFtpServer</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
With this one you can simulate all sorts of behaviors like permission problems, etc:
Example:
fakeFtpServer = new FakeFtpServer();
fakeFtpServer.setServerControlPort(FTPPORT);
FileSystem fileSystem = new UnixFakeFileSystem();
fileSystem.add(new DirectoryEntry(FTPDIRECTORY));
fakeFtpServer.setFileSystem(fileSystem);
fakeFtpServer.addUserAccount(new UserAccount(USERNAME, PASSWORD, FTPDIRECTORY));
...
assertTrue("Expected file to be transferred", fakeFtpServer.getFileSystem().exists(FTPDIRECTORY + "/" + FILENAME));
take a look at this unit test and those in the same directory...they'll show you how to standup a local FTP server for testing and how to use CamelTestSupport to validate scenarios against it, etc...
example unit test...
https://svn.apache.org/repos/asf/camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FromFileToFtpTest.java
which extends this test support class...
https://svn.apache.org/repos/asf/camel/trunk/components/camel-ftp/src/test/java/org/apache/camel/component/file/remote/FtpsServerTestSupport.java
In our project we do not create mock FTP Server to test the route but we use properties that can be replaced by a file Camel Component for the local development and unit testing.
Your code would look like this:
public class MyRoute extends RouteBuilder
{
#Override
public void configure()
{
onException(EdiOrderParsingException.class)
.handled(true)
.to("{{myroute.error}}");
from("{{myroute.input.endpoint}}")
.bean(new OrderEdiTocXml())
.convertBodyTo(String.class)
.convertBodyTo(Document.class)
.choice()
.when(xpath("/cXML/Response/Status/#text='OK'"))
.to("{{myroute.valid.endpoint}}}")
.otherwise()
.to("{{myroute.invalid.endpoint}}");
}
}
And locally and for system test we use a file endpoint declared in the property file:
myroute.input.endpoint=file:/home/user/myproject/input
myroute.valid.endpoint=file:/home/user/myproject/valid
myroute.invalid.endpoint=file:/home/user/myproject/invalid
myroute.error=file:/home/user/myproject/error
or in a JUnit CamelTestSupport you can use the useOverridePropertiesWithPropertiesComponent method to set the properties you want to overrides.
As an alternative you can also use a "direct" route instead but you can miss some File options that can be tested by the unit test.
And we only test the FTP connection with the real system by setting the properties like this:
myroute.input.endpoint=ftp://hostname/input
myroute.valid.endpoint=ftp://hostname/valid
myroute.invalid.endpoint=ftp://hostname/invalid
myroute.error=ftp://hostname/error
With this you can also have different configuration for e.g production server that will differentiate from the Integration Test Environment.
Example of Properties for Production environment:
myroute.input.endpoint=ftp://hostname-prod/input
myroute.valid.endpoint=ftp://hostname-prod/valid
myroute.invalid.endpoint=ftp://hostname-prod/invalid
myroute.error=ftp://hostname-prod/error
In my opinion it is totally acceptable to use file endpoint to simplify the JUnit code and it will test the route only and not the connection.
Testing the connection is more like an Integration Test and should be executed on the real server connected with the real external system (in your case FTP servers, but can be other endpoints/systems as well).
By using properties you can also configure different URL's per environment (For example: we have 3 testing environments and one production environment, all with different endpoints).
Can I write a module/filter that gets put into the processing pipleline in Tomcat BEFORE the web application even gets run?
Something that I could turn on/off for each web application that tomcat is handling.
is this possible?
So basically it would be a re-usable filter that hooks into the web pipeline that could alter the requests behavior or perform/modify the requests. One example would be to log all ip's, or redirect based on the url, block the request, etc.
If you are using Servlet 3.0 you can. What you do is implement either a ServletContextListener or a ServletContainerInitializer. The code below shows withServletContextListener
#WebListener("auto config listeners")
public class MyListenerConfigurator implements ServletContextListener {
public void contextInitialized(ServletContextEvent scEvt) {
ServletContext ctx = scEvt.getServletContext();
FilterRegistration.Dynamic reg = ctx.addFilter("myFilter", "my.listener.class.MyListener");
...
}
See EE6 docs here. Perhaps the only drawback is that you can add but you cannot remove. And you can only at when the application starts up.
Note: code not tested
Have you considered a ServletContextListener in web.xml?