I created uber jar with embedded jetty server(I updated to 9.4.20.v20190813). The fat app includes two parts: the static front-end, and the api backend. The code skeleton copied from jetty server 9.1 multiple embeded ports and application in same server instance. And I want to add GzipHandler on both two web apps.
public class Main
{
public static void main(String[] args)
{
Server server = new Server();
ServerConnector connectorA = new ServerConnector(server);
connectorA.setPort(8080);
connectorA.setName("connA"); // connector name A (static web app)
ServerConnector connectorB = new ServerConnector(server);
connectorB.setPort(9090);
connectorB.setName("connB"); // connector name B (api app)
server.addConnector(connectorA);
server.addConnector(connectorB);
// Basic handler collection
HandlerCollection contexts = new HandlerCollection();
server.setHandler(contexts);
// WebApp A
WebAppContext appA = new WebAppContext();
appA.setContextPath("/a");
appA.setWar("./webapps/webapp-a.war");
appA.setVirtualHosts(new String[]{"#connA"}); // connector name A
contexts.addHandler(appA);
// WebApp B
WebAppContext appB = new WebAppContext();
appB.setContextPath("/b");
appB.setWar("./webapps/webapp-b.war");
appB.setVirtualHosts(new String[]{"#connB"}); // connector name B
contexts.addHandler(appB);
GzipHandler gzipHandler = new GzipHandler();
gzipHandler.setIncludedMethods("POST", "GET");
gzipHandler.setIncludedMimeTypes("text/html", "text/plain", "text/xml", "text/css", "application/javascript",
"text/javascript", "application/json");
gzipHandler.setInflateBufferSize(2048);
gzipHandler.setMinGzipSize(2048);
contexts.addHandler(gzipHandler);
try
{
server.start(); // start server thread
server.join(); // wait for server thread to end
}
catch (Throwable t)
{
t.printStackTrace(System.err);
}
}
}
When I visit http://localhost:8080, some resources return Status Code: 206 Partial Content. , the server throws exception:
17:16:31.447 [qtp60830820-16] WARN org.eclipse.jetty.server.HttpChannel - /favicon.ico
java.lang.NullPointerException: null
at org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:725)
at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:126)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
at org.eclipse.jetty.server.Server.handle(Server.java:502)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:370)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:267)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:305)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:103)
at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:117)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:333)
My question: favicon.ico's mime-type is not included in GzipHandler, and why GzipHandler processes it? and favicon.ico is in the static resources web app root dir (the same level as index.html).
How can I apply the gziphandler correctly? Thanks!
BTW: gzip filter works fine
EDITS:
I upgraded jetty version from 9.4.12.v20180830 to 9.4.20.v20190813
I added more settings on GzipHandler
Jetty 9.1 is EOL (End of Life).
https://www.eclipse.org/jetty/documentation/current/what-jetty-version.html
Your code works, as-is, just upgrade to a supported version of Jetty.
I tested with Jetty 9.4.20.v20190813 and it worked as designed.
The Status Code: 206 Partial Content is an expected status response code, when the response includes partial range data, which is only produced when the request includes the Range: header.
Your code has a handler tree that looks like the following ..
Server.setHandler
\- HandlerCollection
\- WebAppContext ("/a")
\- WebAppContext ("/b")
\- GzipHandler
Having the GzipHandler at the end will allow the GzipHandler to apply to requests that don't match contexts "/a" nor "/b", which is a legitimate configuration, but probably not what you want.
Since you mentioned favicon.ico, that's usually a root request, which wouldn't match either "/a" nor "/b", so that's what we can only assume is what you want.
But, you didn't actually say that you wanted the GzipHandler to apply to the WebAppContext, so I'm just going to assume that is what you really wanted to accomplish.
If that's the case, then we need GzipHandler to do what it needs BEFORE a WebAppContext is entered.
We want a handler tree that looks like this ...
Server.setHandler
\- HandlerList
\- GzipHandler
| \- ContextHandlerCollection
| \- WebAppContext ("/a")
| \- WebAppContext ("/b")
\- DefaultHandler
Which would be a single GzipHandler configured for all contexts.
The rough code would look like this ...
HandlerList handlers = new HandlerList();
server.setHandler(handlers);
WebAppContext appA = new WebAppContext();
appA.setContext("/a");
WebAppContext appB = new WebAppContext();
appB.setContextPath("/b");
ContextHandlerCollection contexts = new ContextHandlerCollection(appA, appB);
GzipHandler gzipHandler = new GzipHandler();
gzipHandler.setHandler(contexts);
handlers.addHandler(gzipHandler);
handlers.addHandler(new DefaultHandler());
But you can also have a unique GzipHandler per WebApp.
Looking like this ...
Server.setHandler
\- HandlerList
\- ContextHandlerCollection
| \- WebAppContext ("/a")
| \- GzipHandler (instance / config A)
| \- WebAppContext ("/b")
| \- GzipHandler (instance / config B)
\- DefaultHandler
or like this ...
Server.setHandler
\- HandlerList
\- GzipHandler (instance / config A)
| \- WebAppContext ("/a")
\- GzipHandler (instance / config B)
| \- WebAppContext ("/b")
\- DefaultHandler
Related
Is WSO2IS impacted by CVE-2021-44228 ?
If so, could we an update on https://docs.wso2.com/display/Security/2021+Advisories (on what versions are affected, and what the recommend remediation is)?
Ensure that you have "zip" and "unzip" commands installed on the server hosting the product.
Option 1: Navigate to the product-home folder and run the following command:
curl https://raw.githubusercontent.com/wso2/security-tools/master/internal/update-scripts/CVE-2021-44228-mitigation.sh | bash
Option 2: Or else download the script from https://raw.githubusercontent.com/wso2/security-tools/master/internal/update-scripts/CVE-2021-44228-mitigation.sh, copy the script into the product-home, and run the script from product-home, using the following command:
bash CVE-2021-44228-mitigation.sh
This is applicable to WSO2 Identity Server 5.9.0 and above versions only. There is a public announcement made by WSO2 on this.
https://docs.wso2.com/pages/viewpage.action?pageId=180948677
What about the plugins/extensions using some wso2 libraries further using log4j2 core?
For example: org.wso2.carbon:org.wso2.carbon.utils:jar:4.6.1 having pax logging as dependency which eventually has log4j 2.12.1 as provided dependency.
[INFO] +- org.wso2.carbon:org.wso2.carbon.utils:jar:4.6.1:compile
[INFO] | +- org.wso2.orbit.org.bouncycastle:bcprov-jdk15on:jar:1.60.0.wso2v1:compile
[INFO] | | \- org.bouncycastle:bcprov-jdk15on:jar:1.60:compile
[INFO] | +- org.wso2.carbon:org.wso2.carbon.user.api:jar:4.6.1:compile
[INFO] | | \- (org.wso2.carbon:org.wso2.carbon.base:jar:4.6.1:compile - omitted for duplicate)
[INFO] | +- org.wso2.carbon:org.wso2.carbon.bootstrap:jar:4.6.1:compile
[INFO] | | +- (org.ops4j.pax.logging:pax-logging-api:jar:1.11.3:compile - omitted for duplicate)
[INFO] | | \- wrapper:wrapper:jar:3.2.3:compile
[INFO] | +- org.ops4j.pax.logging:pax-logging-api:jar:1.11.3:compile
[INFO] | | +- org.osgi:osgi.core:jar:6.0.0:compile
[INFO] | | \- org.osgi:osgi.cmpn:jar:6.0.0:compile
[INFO] | +- **org.ops4j.pax.logging:pax-logging-log4j2:jar:1.11.3:compile**
[INFO] | | +- (org.ops4j.pax.logging:pax-logging-api:jar:1.11.3:compile - omitted for duplicate)
[INFO] | | +- (org.osgi:osgi.core:jar:6.0.0:compile - omitted for duplicate)
[INFO] | | \- (org.osgi:osgi.cmpn:jar:6.0.0:compile - omitted for duplicate)
This is irrespective of wso2 IS version.
I am trying to start Jetty in embedded mode to deploy a war file. I am using jetty lib versioned 9.4.6
I have following task created in Gradle for starting Jetty and deploying the web application.
println 'Starting Jetty............'
project.ext.server = new Server();
ServerConnector connector = new ServerConnector(project.ext.server);
connector.setPort(jettyPort);
project.ext.server.addConnector(connector);
WebAppContext webapp = new WebAppContext()
webapp.setContextPath('/')
def warPath = 'build/libs/';
warPath += 'test-' + project.version + '.war';
println("Deploying WAR File : --> ${warPath}");
webapp.setWar(warPath)
project.ext.server.setHandler(webapp);
project.ext.server.start();
println 'Server started, waiting...'
new StopMonitor(jettyStopPort, project.ext.server).start();
println 'Jetty started.'
but above script fails with following error
Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.eclipse.jetty.server.session.SessionHandler
Exact line from the script which is failing is
WebAppContext webapp = new WebAppContext()
Even if I keep this line as the single line in the script and remove everything, I get the same error.
Interestingly, the class for which it is complaining is present in the jar file jetty-server. Same script used to work with jetty libs 8.1
Note: In order to make the script work with jetty 9.4, i had to use ServerConnector class instead of BlockingConnectot, which was removed in jetty 9.4, rest of the script is same.
I am not sure why this failing.
You are probably missing required jar files.
Would strongly encourage you to use a proper build tool, you have many to choose from.
Here's the jar dependency list (in tree form) for the example project at...
https://github.com/jetty-project/embedded-servlet-3.1
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) # embedded-servlet-3.1 ---
[INFO] org.eclipse.jetty.demo:embedded-servlet-3.1:war:1-SNAPSHOT
[INFO] +- javax.servlet:javax.servlet-api:jar:3.1.0:compile
[INFO] +- org.eclipse.jetty:jetty-webapp:jar:9.4.6.v20170531:compile
[INFO] | +- org.eclipse.jetty:jetty-xml:jar:9.4.6.v20170531:compile
[INFO] | | \- org.eclipse.jetty:jetty-util:jar:9.4.6.v20170531:compile
[INFO] | \- org.eclipse.jetty:jetty-servlet:jar:9.4.6.v20170531:compile
[INFO] | \- org.eclipse.jetty:jetty-security:jar:9.4.6.v20170531:compile
[INFO] | \- org.eclipse.jetty:jetty-server:jar:9.4.6.v20170531:compile
[INFO] | +- org.eclipse.jetty:jetty-http:jar:9.4.6.v20170531:compile
[INFO] | \- org.eclipse.jetty:jetty-io:jar:9.4.6.v20170531:compile
[INFO] \- org.eclipse.jetty:jetty-annotations:jar:9.4.6.v20170531:compile
[INFO] +- org.eclipse.jetty:jetty-plus:jar:9.4.6.v20170531:compile
[INFO] | \- org.eclipse.jetty:jetty-jndi:jar:9.4.6.v20170531:compile
[INFO] +- javax.annotation:javax.annotation-api:jar:1.2:compile
[INFO] +- org.ow2.asm:asm:jar:5.1:compile
[INFO] \- org.ow2.asm:asm-commons:jar:5.1:compile
[INFO] \- org.ow2.asm:asm-tree:jar:5.1:compile
Collegues, i call webservice and receive the exception:
org.springframework.ws.soap.SoapMessageCreationException: Could not
create message from InputStream: Invalid Content-Type:text/html. Is
this an error message instead of a SOAP response?; nested exception is
com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Invalid
Content-Type:text/html. Is this an error message instead of a SOAP
response?
I read this and this.
Wireshark shows than i receive response:
HTTP/1.1 200 OK
Content-Type: text/html
Transfer-Encoding: chunked
Server: Jetty(8.1.13.v20130916)
<?xml version="1.0" encoding="UTF-8"?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ws="http://mayacomp/Generic/Ws">
<soapenv:Body>
<ws:response>
<out>
<requestID>6b140165-af79-47a2-9e9f-5b7bba265050</requestID>
<requestTimestamp>2015-12-01T13:04:44.044</requestTimestamp>
</out>
</ws:response>
</soapenv:Body>
</soapenv:Envelope>
My beans looks like:
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.mayacomp.entities");
HashMap<String,Object> properties = new HashMap<String,Object>();
properties.put(javax.xml.bind.Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.setMarshallerProperties(properties);
return marshaller;
}
#Bean
public myClient myClient(Jaxb2Marshaller marshaller) {
myClient client = new myClient();
client.setDefaultUri("URL");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
//client.setMessageSender(messageSender);
return client;
}
Class which calls WS looks like:
public class myClient extends WebServiceGatewaySupport {
public Response createApplication(In in) {
Request request = (Request) new Request();
request.setIn(in);
Response response = (Response) getWebServiceTemplate().marshalSendAndReceive(
"URL",
request,
new WebServiceMessageCallback()
{ public void doWithMessage(WebServiceMessage message) {
SaajSoapMessage saajSoapMessage = (SaajSoapMessage)message;
SOAPMessage soapMessage = SOAP.createSOAPMessage(in);
saajSoapMessage.setSaajMessage(soapMessage);
}
}
);
return null;
}
Could you help me to read web service response?
UPDATE
--- maven-dependency-plugin:2.8:tree (default-cli) # adapter ---
com.comp:adapter:jar:0.0.1-SNAPSHOT
+- junit:junit:jar:4.12:compile
| \- org.hamcrest:hamcrest-core:jar:1.3:compile
+- org.springframework:spring-context:jar:4.2.3.RELEASE:compile
| +- org.springframework:spring-aop:jar:4.2.3.RELEASE:compile
| | \- aopalliance:aopalliance:jar:1.0:compile
| +- org.springframework:spring-beans:jar:4.2.3.RELEASE:compile
| \- org.springframework:spring-expression:jar:4.2.3.RELEASE:compile
+- org.springframework:spring-core:jar:4.2.3.RELEASE:compile
| \- commons-logging:commons-logging:jar:1.2:compile
+- io.codearte.jfairy:jfairy:jar:0.5.1:compile
| +- com.google.guava:guava:jar:19.0-rc2:compile (version selected from constraint [15.0,))
| +- org.yaml:snakeyaml:jar:1.16:compile (version selected from constraint [1.9,2.0))
| +- org.iban4j:iban4j:jar:2.1.1:compile
| +- com.google.inject:guice:jar:4.0:compile
| | \- javax.inject:javax.inject:jar:1:compile
| +- joda-time:joda-time:jar:2.3:compile
| +- com.google.inject.extensions:guice-assistedinject:jar:4.0:compile
| +- org.reflections:reflections:jar:0.9.9:compile
| | +- org.javassist:javassist:jar:3.18.2-GA:compile
| | \- com.google.code.findbugs:annotations:jar:2.0.1:compile
| +- org.apache.commons:commons-lang3:jar:3.3.2:compile
| \- org.slf4j:slf4j-api:jar:1.7.7:compile
+- org.fluttercode.datafactory:datafactory:jar:0.8:compile
+- org.springframework.ws:spring-ws-core:jar:2.2.3.RELEASE:compile
| +- org.springframework.ws:spring-xml:jar:2.2.3.RELEASE:compile
| +- org.springframework:spring-oxm:jar:4.0.9.RELEASE:compile
| +- org.springframework:spring-web:jar:4.0.9.RELEASE:compile
| \- org.springframework:spring-webmvc:jar:4.0.9.RELEASE:compile
+- log4j:log4j:jar:1.2.17:compile
\- org.apache.httpcomponents:httpclient:jar:4.5.1:compile
+- org.apache.httpcomponents:httpcore:jar:4.4.3:compile
\- commons-codec:commons-codec:jar:1.9:compile
I am facing the same problem with one of our vendor services which returns Content-Type as text/html and spring web service template does not like that.
As there is no easy way to change the response headers in spring web-service. I solved it by using custom Http client and then providing client interceptor which removes "Content-Type" headers.
HttpClient httpClient = HttpClients.custom()
.addInterceptorFirst(new HttpComponentsMessageSender.RemoveSoapHeadersInterceptor())
.addInterceptorFirst(new HttpResponseInterceptor() {
#Override
public void process(HttpResponse httpResponse, HttpContext httpContext) throws HttpException, IOException {
boolean htmlResponse = Arrays.stream(httpResponse.getAllHeaders()).anyMatch(header -> header.getName().equalsIgnoreCase("Content-Type") && header.getValue().contains("text/html"));
if (htmlResponse) {
httpResponse.removeHeaders("Content-Type");
}
}
})
.setDefaultRequestConfig(requestConfig)
.build();
I just removed and did not add "Content-Type" header because SOAPMessageFactory for SOAP 1.1 and 1.2 adds a appropriate header before processing the message.
Take a look in your stack trace where org.springframework.ws.soap.SoapMessageCreationException is being thrown. Depending on where the error happens you might be able to configure org.springframework.ws.client.core.WebServiceTemplate to accept Content-Type:text/html.
I have a feeling that that aspect of the implementation is not pluggable and that you wouldn't be able to replace it. See SOAPExceptionImpl: Invalid Content-Type:text/html. Is this an error message instead of a SOAP response?. The base exception is occuring in some jdk code.
If it were me, I'd be tempted to use a regular http client, make an http call, and manually parse the response. There are some complete examples for using the apache client to do this here: Sending HTTP Post request with SOAP action using org.apache.http
I had the same problem as you with this Content-Type:text/html
So all my focus was on trying to change the CT to text/xml but that didn't work. The problem is your "MyClient" you need to have a correct "MessageSender" and dependancy that I have added in my pom.
#Bean
public HttpComponentsMessageSender defaultMessageSender() {
HttpComponentsMessageSender messageSender = createMessageSenderBasicAuth();
HttpClientBuilder httpClientBuilder = HttpClients.custom()
.useSystemProperties()
.addInterceptorFirst(new HttpComponentsMessageSender.RemoveSoapHeadersInterceptor())
.setDefaultCredentialsProvider(basicAuthCredentials())
.setDefaultRequestConfig(requestConfigWithDefaultTimeout());
messageSender.setHttpClient(httpClientBuilder.build());
return messageSender;
}
private HttpComponentsMessageSender createMessageSenderBasicAuth() {
return new HttpComponentsMessageSender() {
#Override
protected HttpContext createContext(URI uri) {
HttpHost target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
AuthCache authCache = new BasicAuthCache();
BasicScheme basicAuth = new BasicScheme();
authCache.put(target, basicAuth);
HttpClientContext localContext = HttpClientContext.create();
localContext.setAuthCache(authCache);
return localContext;
}
};
}
private RequestConfig requestConfigWithDefaultTimeout() {
return RequestConfig.custom()
.setConnectionRequestTimeout(10000)
.setConnectTimeout(10000) // 10 sec
.setSocketTimeout(10000)
.build();
}
In the pom file I have added
pom.xml
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-ws-core</artifactId>
<version>2.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>
Your "MyClient" Should look like this
#Bean
public myClient myClient(#Qualifier("marshaller") Jaxb2Marshaller marshaller) {
myClient client = new myClient();
client.setDefaultUri("URL");
client.setMarshaller(marshaller);
client.setUnmarshaller(marshaller);
client.setMessageSender(defaultMessageSender());
return client;
}
And its a good idea to call
WebServiceTemplate webServiceTemplate = getWebServiceTemplate();
webServiceTemplate.afterPropertiesSet();
inside your "public class myClient extends WebServiceGatewaySupport" class
Setup: I have set up embedded jetty (v9.1) to serve static files with the setDirectoriesListed(true) and the code I am using is below:
// Create a basic Jetty server object that will listen on port 8080. Note that if you set this to port 0
// then a randomly available port will be assigned that you can either look in the logs for the port,
// or programmatically obtain it for use in test cases.
Server server = new Server(9090);
// Create the ResourceHandler. It is the object that will actually handle the request for a given file. It is
// a Jetty Handler object so it is suitable for chaining with other handlers as you will see in other examples.
ResourceHandler resource_handler = new ResourceHandler();
// Configure the ResourceHandler. Setting the resource base indicates where the files should be served out of.
// In this example it is the current directory but it can be configured to anything that the jvm has access to.
resource_handler.setDirectoriesListed(true);
resource_handler.setWelcomeFiles(new String[]{ "index.html" });
resource_handler.setResourceBase(".");
// Add the ResourceHandler to the server.
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { resource_handler, new DefaultHandler() });
server.setHandler(handlers);
// Start things up! By using the server.join() the server thread will join with the current thread.
// See "http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.html#join()" for more details.
server.start();
server.join();
This code is originally from here.
When I navigate to the address http://localhost:9090/, I see the files listed in the directory and I am able to click and open individual text files.
Problem: For some inexplicable reason, only when I click on a file of 0 bytes (aka it's an empty file, but still shown in the browser), the connection tries to load but eventually times out (30 seconds) and I get a response in safari saying the "server unexpectedly dropped the connection." In addition, when I make a HttpURLConnection to the 0 byte file, I get a content length returned of -1; This of course is only for empty files.
Expected Behavior as seen in standalone Jetty: When I use standalone jetty and serve the same files, I am able to "open" the empty file which just returns a blank page in a web browser. When using the HttpURLConnection, I get a content length of 0.
While this seems like a "pointless" task, one server is programmatically syncing with the embedded jetty server (so I want those empty files to sync). I imagine it has something to do with the resource handler seeing 0 bytes as it serves the static content, but I'm not too sure how to get the same behavior of the standalone jetty server as right now, it errors when trying to pull the empty files.
Thanks!
Your code works, as-is, at least on Jetty 9.2.7.v20140116
Full example I used:
package jetty;
import java.io.File;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.DefaultHandler;
import org.eclipse.jetty.server.handler.HandlerList;
import org.eclipse.jetty.server.handler.ResourceHandler;
public class SimpleResources
{
public static void main(String[] args)
{
Server server = new Server(9090);
String resourceBase = System.getProperty("resourceBase", ".");
System.err.printf("Resource Base is: %s%n", new File(resourceBase).getAbsolutePath());
ResourceHandler resource_handler = new ResourceHandler();
resource_handler.setDirectoriesListed(true);
resource_handler.setWelcomeFiles(new String[] { "index.html" });
resource_handler.setResourceBase(resourceBase);
HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { resource_handler, new DefaultHandler() });
server.setHandler(handlers);
try
{
server.start();
server.join();
}
catch (Throwable t)
{
t.printStackTrace(System.err);
}
}
}
I ran it pointing the -DresourceBase system property to a directory that has the following ...
$ ls -la
total 8
drwxrwxr-x. 2 joakim joakim 4096 Jan 20 11:53 .
drwxrwxr-x. 3 joakim joakim 4096 Jan 20 11:53 ..
-rw-rw-r--. 1 joakim joakim 0 Jan 20 11:53 foo.txt
And once running the console shows ...
2015-01-20 11:55:07.788:INFO::main: Logging initialized #68ms
Resource Base is: /home/joakim/code/Jetty/empties
2015-01-20 11:55:07.837:INFO:oejs.Server:main: jetty-9.2.7.v20150116
2015-01-20 11:55:07.860:INFO:oejs.ServerConnector:main: Started ServerConnector#5461eda{HTTP/1.1}{0.0.0.0:9090}
2015-01-20 11:55:07.861:INFO:oejs.Server:main: Started #144ms
With a test request like such ...
$ curl --dump-header - http://localhost:9090/foo.txt
HTTP/1.1 200 OK
Date: Tue, 20 Jan 2015 18:55:39 GMT
Content-Type: text/plain
Content-Length: 0
Server: Jetty(9.2.7.v20150116)
Update:
Works as-is with no modifications on the following versions of jetty as well (didn't do an exhaustive test of versions, just a few older ones as well)
9.2.6.v20141205 - Identical Results
9.2.4.v20141103 - Identical Results
9.2.1.v20140609 - Identical Results
9.1.5.v20140505 - No Date in response headers, rest is the same (yes, it also sends Content-Length: 0)
Using Hibernate EntityManager 3.5.3-Final together with Unitils 3.1 results in:
unitilsAfterTestTearDown(com.unifiedpost.payments.model.TestAccount) Time elapsed: 0.02 sec <<< FAILURE!
java.lang.AbstractMethodError: org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo.getValidationMode()Ljavax/persistence/ValidationMode;
at org.hibernate.ejb.Ejb3Configuration.configure(Ejb3Configuration.java:621)
at org.unitils.orm.jpa.util.provider.hibernate.UnitilsHibernatePersistenceProvider.createContainerEntityManagerFactory(UnitilsHibernatePersistenceProvider.java:47)
at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:227)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:281)
at org.unitils.orm.jpa.util.JpaEntityManagerFactoryLoader.createEntityManagerFactoryBean(JpaEntityManagerFactoryLoader.java:77)
This is also reported in: http://jira.unitils.org/browse/UNI-201
The reason is that unitils-orm depends on a 2.5.x release of Spring, which is not JPA2 ready yet.
[INFO] +- org.unitils:unitils-testng:jar:3.1:test
[INFO] +- org.unitils:unitils-orm:jar:3.1:test
[INFO] | +- org.unitils:unitils-spring:jar:3.1:test
[INFO] | | +- org.springframework:spring-core:jar:2.5.2:test
[INFO] | | +- org.springframework:spring-beans:jar:2.5.2:test
[INFO] | | +- org.springframework:spring-test:jar:2.5.2:test
[INFO] | | \- org.springframework:spring-tx:jar:2.5.2:test
See also:
http://jira.springframework.org/browse/SPR-6408
http://jira.springframework.org/browse/SPR-6705
You have 2 workaround options:
(1) Patch Unitils:
Checkout Unitils (http://www.unitils.org/source-repository.html)
Fix the root pom: Make it depend on a 3.0.1.RELEASE (or higher)
Build a release locally
Use this as your new Unitils package
<properties>
<spring.version>3.0.3.RELEASE</spring.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
(2) Include a patched MutablePersistenceUnitInfo
Duplicate Spring MutablePersistenceUnitInfo in your project (and make sure it preceeds the legacy Spring version in the classpath)
Add the missing methods to it.
#Override
public String getPersistenceXMLSchemaVersion() {
return "1.0";
}
#Override
public SharedCacheMode getSharedCacheMode() {
return SharedCacheMode.NONE;
}
#Override
public ValidationMode getValidationMode() {
return ValidationMode.NONE;
}
[Answered myself for later reference]
Also not a direct answer to the original question, but the trunk version (4.0-SNAPSHOT) has a Spring 3.0 dependency. Under the hood the JPA support will be re-engineered to use Spring (1).