httpunit PutMethodWebRequest throws IOException; bad file descriptor - rest

Could someone explain why this httpunit test case keeps failing in wc.getResponse with "bad file descriptor". I added the is.close() as a guess and moved it before and after the failure but that had no effect. This tests put requests to a Dropwizard app.
public class TestCircuitRequests
{
static WebConversation wc = new WebConversation();
static String url = "http://localhost:8888/funl/circuit/test.circuit1";
#Test
public void testPut() throws Exception
{
InputStream is = new FileInputStream("src/test/resources/TestCircuit.json");
WebRequest rq = new PutMethodWebRequest(url, is, "application/json");
wc.setAuthentication("FUNL", "foo", "bar");
WebResponse response = wc.getResponse(rq);
is.close();
}

No responses? So I'll try myself based on what I learned fighting this.
Httpunit is an old familiar tool that I'd use if I could. But it hasn't been updated in more than two years, so I gather its support for #PUT requests isn't right.
So I converted to Jersey-client instead. After a bunch of struggling I wound up with this code which does seem to work:
#Test
public void testPut() throws Exception
{
InputStream is = new FileInputStream("src/test/resources/TestCircuit.json");
String circuit = StreamUtil.readFully(is);
is.close();
Authenticator.setDefault(new MyAuthenticator());
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
com.sun.jersey.api.client.WebResource service = client.resource(url);
Builder builder = service.accept(MediaType.APPLICATION_JSON);
builder.entity(circuit, MediaType.APPLICATION_JSON);
builder.put(String.class, circuit);
return;
}
This intentionally avoids JAX-RS automatic construction of beans from JSON strings.

Related

Apache FOP: upgrading from 1.1 to 2.1

I am following the migration guide, but I don't seem to get it right.
In FOP 1.1 I have this working code:
public class XsltFactory {
private static final String FO_CONFIG_FILE = "/path/to/fop-config.xml";
private static FopFactory fopFactory;
private static synchronized void initFopFactory(final ServletContext context) throws Exception {
Configuration cfg = new DefaultConfigurationBuilder().build(XsltFactory.class.getResourceAsStream(FO_CONFIG_FILE));
fopFactory = FopFactory.newInstance();
fopFactory.setURIResolver(new ServletContextURIResolver(context));
fopFactory.setUserConfig(cfg);
}
}
I adapted the above code to stick with FOP 2.1:
public class XsltFactory {
private static final String FO_CONFIG_FILE = "/path/to/fop-config.xml";
private static FopFactory fopFactory;
private static synchronized void initFopFactory(final ServletContext context) throws Exception {
Configuration cfg = new DefaultConfigurationBuilder().build(XsltFactory.class.getResourceAsStream(FO_CONFIG_FILE));
FopFactoryBuilder fopFactoryBuilder = new FopFactoryBuilder(
new URI(ServletContextURIResolver.SERVLET_CONTEXT_PROTOCOL),
new URIResolverAdapter(new ServletContextURIResolver(context))
);
fopFactoryBuilder.setConfiguration(cfg);
fopFactory = fopFactoryBuilder.build();
}
}
But I get the following error:
java.lang.Exception: Fail to create PDF
at ....web.controller.PrintPdfController.renderPdf(PrintPdfController.java:181)
[...]
at weblogic.work.ExecuteThread.run(ExecuteThread.java:263)
Caused by: java.net.URISyntaxException: Expected scheme-specific part at index 16: servlet-context:
at java.net.URI$Parser.fail(URI.java:2829)
at java.net.URI$Parser.failExpecting(URI.java:2835)
at java.net.URI$Parser.parse(URI.java:3038)
at java.net.URI.<init>(URI.java:595)
[...]
... 42 common frames omitted
The PDF fails to load, since it failed at being created.
EDIT:
After adding + "///" after SERVLET_CONTEXT_PROTOCOL the context, I now get:
Caused by: java.net.MalformedURLException: unknown protocol: servlet-context
at java.net.URL.<init>(URL.java:592)
at java.net.URL.<init>(URL.java:482)
at java.net.URL.<init>(URL.java:431)
at java.net.URI.toURL(URI.java:1096)
at org.apache.fop.fonts.FontDetectorFactory$DefaultFontDetector.detect(FontDetectorFactory.java:94)
... 59 common frames omitted
After a few days of investigation, the migration has finally been done successfully. The problem was coming from the URI resolver, and fixing this problem created new problems, which I solved subsequently.
The guide at https://xmlgraphics.apache.org/fop/2.1/upgrading.html is of relatively limited help.
The core of the problem is the URI resolver. You now have to define a custom resolver, but NOT as in the example provided at:
https://xmlgraphics.apache.org/fop/2.0/servlets.html
ResourceResolver resolver = new ResourceResolver() {
public OutputStream getOutputStream(URI uri) throws IOException {
URL url = getServletContext().getResource(uri.toASCIIString());
return url.openConnection().getOutputStream();
}
public Resource getResource(URI uri) throws IOException {
return new Resource(getServletContext().getResourceAsStream(uri.toASCIIString()));
}
};
The right way of doing it is:
ResourceResolver resolver = new ResourceResolver() {
public OutputStream getOutputStream(URI uri) throws IOException {
URL url = context.getResource(uri.getPath());
return url.openConnection().getOutputStream();
}
public Resource getResource(URI uri) throws IOException {
return new Resource(context.getResourceAsStream(uri.getPath()));
}
};
Instead of uri.toASCIIString(), the correct syntax is uri.getPath().
In addition, we had to remove all "servlet-context:" markup in fonts URIs (in fop-config.xml) and images URIs (in any XSL transformation file or template).
Finally, I got an issue with hyphenation: FOP could not find .hyp files anymore, because for some reason, the baseUri was being used instead of the custom context resolver (I had to dig into FOP's source files to find out). So, I had to modify the getResource method of my custom resolver. I know this is a hack, but it works and it is sufficient for me as I already spent three days on this problem):
public OutputStream getOutputStream(URI uri) throws IOException {
URL url = context.getResource(uri.getPath());
return url.openConnection().getOutputStream();
}
public Resource getResource(URI uri) throws IOException {
InputStream stream = null;
/*
* For some reason, in FOP 2.x, the hyphenator does not use the
* classpath fop-hyph.jar.
*
* This causes trouble as FOP tries to find "none.hyp" in the
* war directory. Setting
* <hyphenation-base>/WEB-INF/hyph</hyphenation-base> in the
* fop-config.xml file does not solve the issue. The only
* solution I could find is to programmatically detect when a
* .hyp file is trying to be loaded. When this occurs, I modify
* the path so that the resolver gets the right resource.
*
* This is a hack, but after spending three days on it, I just
* went straight to the point and got a workaround.
*/
if (uri.getPath().endsWith('.hyp')) {
String relUri = uri.getPath().substring(uri.getPath().indexOf(baseUri.getPath()) + baseUri.getPath().length());
stream = context.getResourceAsStream(FopManager.HYPH_DIR + relUri);
} else {
stream = context.getResourceAsStream(uri.getPath());
}
Resource res = new Resource(stream);
return res;
}
};
Note that I also had to create the none.hyp file manually, since it does not exist in the .hyp files provided by OFFO. I just copied en.hyp and renamed it none.hyp. This solved my last problem.
I hope this saves someone a few days of work ;)

Is RESTEasy RegisterBuiltin.register necessary when using ClientResponse<T>

I am developing a REST client using JBOSS app server and RESTEasy 2.3.6. I've included the following line at the beginning of my code:
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
Here's the rest of the snippet:
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
DefaultHttpClient httpclient = new DefaultHttpClient();
httpclient.getCredentialsProvider().setCredentials(
new AuthScope(host, port, AuthScope.ANY_REALM), new UsernamePasswordCredentials(userid,password));
ClientExecutor executor = createAuthenticatingExecutor(httpclient, host, port);
String uriTemplate = "http://myhost:8080/webapp/rest/MySearch";
ClientRequest request = new ClientRequest(uriTemplate, executor);
request.accept("application/json").queryParameter("query", searchArg);
ClientResponse<SearchResponse> response = null;
List<MyClass> values = null;
try
{
response = request.get(SearchResponse.class);
if (response.getResponseStatus().getStatusCode() != 200)
{
throw new Exception("REST GET failed");
}
SearchResponse searchResp = response.getEntity();
values = searchResp.getValue();
}
catch (ClientResponseFailure e)
{
log.error("REST call failed", e);
}
finally
{
response.releaseConnection();
}
private ClientExecutor createAuthenticatingExecutor(DefaultHttpClient client, String server, int port)
{
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
HttpHost targetHost = new HttpHost(server, port);
authCache.put(targetHost, basicAuth);
// Add AuthCache to the execution context
BasicHttpContext localContext = new BasicHttpContext();
localContext.setAttribute(ClientContext.AUTH_CACHE, authCache);
// Create ClientExecutor.
ApacheHttpClient4Executor executor = new ApacheHttpClient4Executor(client, localContext);
return executor;
}
The above is a fairly simple client that employs the ClientRequest/ClientResponse<T> technique. This is documented here. The above code does work (only left out some trivial variable declarations like host and port). It is unclear to me from the JBOSS documentation as to whether I need to run RegisterBuiltin.register first. If I remove the line completely - my code still functions. Do I really need to include the register method call given the approach I have taken? The Docs say I need to run this once per VM. Secondly, if I am required to call it, is it safe to call more than one time in the same VM?
NOTE: I do understand there are newer versions of RESTEasy for JBOSS, we are not there yet.

Restlet Studio Error 422 when generating sample client and server

Hi I'm using the restlet studio to generate a client and server from your sample pet store API . Here are my steps:
Generate Java Server (JAX-RS)
Edit pom.xml to make a war file
mvn package
Deploy to jetty server as webapp
Verify it works by going to hitting the URL with a browser:
http://54.149.215.125:8080/v2/pet/findByTags
Response:
{"code":4,"type":"ok","message":"magic!"}
At this point I think it works, until I generate the client in Java
I change the endpoint from the webnik one to my webserver
Make a simple main method
public static void main(String[] args) {
try {
FindPetByTagsClientResource a = new FindPetByTagsClientResource();
Pet represent = a.represent();
} catch (Exception ex) {
Logger.getLogger(APIPetStore.class.getName()).log(Level.SEVERE, null, ex);
}
}
When I run it I get this:
run:
Starting the internal HTTP client
null
Unprocessable Entity (422) - The server understands the content type of the request entity and the syntax of the request entity is correct but was unable to process the contained instructions
at org.restlet.resource.Resource.toObject(Resource.java:893)
at org.restlet.engine.resource.ClientInvocationHandler.invoke(ClientInvocationHandler.java:326)
at com.sun.proxy.$Proxy5.represent(Unknown Source)
at net.apispark.webapi.client.FindPetByTagsClientResource.represent(FindPetByTagsClientResource.java:22)
at apipetstore.APIPetStore.main(APIPetStore.java:28)
Caused by: java.io.IOException: Unable to create the Object representation
at org.restlet.engine.converter.DefaultConverter.toObject(DefaultConverter.java:282)
at org.restlet.service.ConverterService.toObject(ConverterService.java:229)
at org.restlet.resource.Resource.toObject(Resource.java:889)
... 4 more
Caused by: java.lang.IllegalArgumentException: The serialized representation must have this media type: application/x-java-serialized-object or this one: application/x-java-serialized-object+xml
at org.restlet.representation.ObjectRepresentation.(ObjectRepresentation.java:221)
at org.restlet.representation.ObjectRepresentation.(ObjectRepresentation.java:123)
at org.restlet.representation.ObjectRepresentation.(ObjectRepresentation.java:104)
at org.restlet.engine.converter.DefaultConverter.toObject(DefaultConverter.java:279)
... 6 more
BUILD SUCCESSFUL (total time: 0 seconds)
Change the main method to this and it works:
public static void main(String[] args) {
try {
FindPetByTagsClientResource a = new FindPetByTagsClientResource();
a.getClientResource().get().write(System.out);
} catch (Exception ex) {
Logger.getLogger(APIPetStore.class.getName()).log(Level.SEVERE, null, ex);
}
}
Output:
Starting the internal HTTP client
{"code":4,"type":"ok","message":"magic!"}
Any ideas on how I can fix this?
In fact, the JAXRS server skeleton is really a server skeleton ;-) This means that it doesn't actually send back the right content according to the client. If you look at the server code, you always see this:
public Response findPetsByTags(#ApiParam(value = "Tags to filter by") #QueryParam("tags") List<String> tags)
throws NotFoundException {
// do some magic!
return Response.ok().entity(new ApiResponseMessage(ApiResponseMessage.OK, "magic!")).build();
}
It doesn't correspond to a list of pet objects...
On the client side, you got the error since you try to use annotated interfaces. They automatically try to use the internal converter of Restlet. It fails since it expects an object of type Pet and you received something with this structure: {"code":4,"type":"ok","message":"magic!"}.
In conclusion, you need to do some work to adapt the server skeleton to return the correct objects. Here is an hardcoded solution to make work your client SDK:
#GET
#Path("/findByTags")
#ApiOperation(value = "Finds Pets by tags", notes = "Finds Pets by tags", response = Pet.class, responseContainer = "List")
#ApiResponses(value = {
#ApiResponse(code = 400, message = "") })
public Response findPetsByTags(#ApiParam(value = "Tags to filter by") #QueryParam("tags") List<String> tags)
throws NotFoundException {
// do some magic!
Pet pet = new Pet();
pet.setId(10);
pet.setName("My pet");
pet.setStatus("status");
List<Tag> actualTags = new ArrayList<Tag>();
Tag tag1 = new Tag();
tag1.setId(1);
tag1.setName("tag1");
actualTags.add(tag1);
Tag tag2 = new Tag();
tag2.setId(2);
tag2.setName("tag2");
actualTags.add(tag2);
pet.setTags(actualTags);
return Response.ok().entity(pet).build();
}
I'll have a look if we can improve this for the server side. In fact, the Restlet Studio internally uses the swagger2 codegen tool chain to generate this server skeleton.
Hope it helps,
Thierry

org.apache.http.NoHttpResponseException: XX.XX.XX.XX:443 failed to respond

Currently I am using Apache http components client V4.3.5. In my case, I can upload small file(1kb), but it is not working on large file(100kb) when I run the code and get the exception "org.apache.http.NoHttpResponseException: 192.168.128.109:443 failed to respond". Can anyone take a look at my code and let me know what causes my issue?
Here is my code:
public static void main(String[] args) throws IOException,
KeyStoreException {
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(
null, new TrustStrategy() {
public boolean isTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
return true;
}
}).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpClientBuilder builder = HttpClientBuilder.create();
builder.disableContentCompression();
builder.setSSLSocketFactory(sslsf);
SocketConfig config = SocketConfig.custom().setSoKeepAlive(true).setSoTimeout(300000).build();
builder.setDefaultSocketConfig(config);
CloseableHttpClient httpclient = builder.build();
HttpPost httppost = new HttpPost("https://192.168.128.109/upload");
String encodedAuthorization = DatatypeConverter
.printBase64Binary("admin:itronitr".getBytes());
httppost.setHeader("Authorization", "Basic " + encodedAuthorization);
FileBody bin = new FileBody(new File("c:\\test.txt"));
String boundary = "hK1oPL5_XSfbm6lkCNlKI63rltrew5Bqik0ul";
HttpEntity reqEntity = MultipartEntityBuilder.create()
.setBoundary(boundary).addPart("upfile", bin).build();
httppost.setEntity(reqEntity);
System.out.println(httppost.getEntity().getContentLength());
System.out
.println(httppost.getEntity().getContentType().toString());
CloseableHttpResponse response = httpclient.execute(httppost);
HttpEntity resEntity = response.getEntity();
System.out.println(response.getStatusLine());
String content = EntityUtils.toString(resEntity);
System.out.println(content);
} catch (Exception exc) {
exc.printStackTrace();
}
}
Thanks,
Bill
Finally I fix the issue and it is caused by buffer size. By default, buffer size of httpclient is 8k. So I change it to 4k and my code works well.
Here is the code that changes buffer size:
ConnectionConfig connectionConfig = ConnectionConfig.custom()
.setBufferSize(4128)
.build();
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultConnectionConfig(connectionConfig)
.build();
This is what worked for me; may or may not work for you!!
I recently encountered the same issue and tried all the suggestions whatever I was able to find on internet i.e upgrading httpClient to latest version and adding a re-try handler ; but none fixed it for me.
I already had a re-try handler built in my code and was running on the latest Apache client, but it was still failing with the exception Caused by: org.apache.http.NoHttpResponseException: xxxxx:443 failed to respond
So, took me almost 2 days to debug this issue and find the root cause (at-least in my case)
There seems to be a bug in older Java versions up to Java 11.0.3 included that prevents Apache HTTP Client from sending payloads bigger than 16368 bytes caused by https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8214339.
I was running on java 11.0.2 and when I upgraded to 11.0.10, it worked for me and I was able to send the bigger payload without any code changes
I also faced the similar problem. I went through many blogs and forums and tried various things but none worked for me. So, I tried a workaround. I added retry handler as below. And it worked for me:
HttpClientBuilder.create()
.setDefaultCredentialsProvider(provider)
.setRetryHandler(new DefaultHttpRequestRetryHandler() {
#Override
public boolean retryRequest(final IOException exception, final int executionCount, final HttpContext context) {
if (exception instanceof NoHttpResponseException) {
return true;
}
return super.retryRequest(exception, executionCount, context);
}
})
.build();
Although it is not a correct fix and just a workaround but it is working for me for now. I'll stick to this solution till I won't get any permanent solution. Sharing it here in case someone might get benefit from it.

Error in uploading a file using Jersey rest service

I am using jersey for building rest service which will upload a file. But I am facing problem in writing a file to required location. Java throws a system cannot find specified path error. Here is my Web service :
#POST
#Path("/fileupload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFile(#FormDataParam("file")InputStream fileUploadStream, #FormDataParam("file")FormDataContentDisposition fileDetails) throws IOException{
StringBuilder uploadFileLocation= new StringBuilder();
uploadFileLocation.append("c:/logparser/webfrontend/uploads");
uploadFileLocation.append("/"+dateFormat.format(Calendar.getInstance().getTime()));
uploadFileLocation.append("/"+fileDetails.getFileName());
writeToFile(fileUploadStream, uploadFileLocation.toString());
return Response.status(200).entity("File saved to " + uploadFileLocation).build();
}
private void writeToFile(InputStream uploadInputStream, String uploadFileLocation)
{
log.debug("UploadService , writeToFile method , start ()");
try{
int read = 0;
byte[] bytes = new byte[uploadInputStream.available()];
log.info("UploadService, writeToFile method , copying uploaded files.");
OutputStream out = new FileOutputStream(new File(uploadFileLocation));
while ((read = uploadInputStream.read(bytes)) != -1)
{
out.write(bytes, 0, read);
}
out.flush();
out.close();
}
catch(Exception e)
{
log.error("UploadService, writeToFile method, error in writing to file "+e.getMessage());
}
}
From looking at just the code (it's usually helpful to include the exception and stack trace), you're trying to write to a directory based on a timestamp which doesn't exist yet. Try adding a call to File.mkdir/mkdirs. See this question/answer: FileNotFoundException (The system cannot find the path specified)
Side note - Unless you have a reason not to, I'd consider using something like Apache commons-io(FileUtils.copyInputStreamToFile) to do the writing.