Uploading jar to nexus with Spring RestTemplate - spring

I would like to use Spring's RestTemplate to upload a jar to nexus. I found instructions on how to do it using curl here, which works just fine. However I have utterly failed in converting this to a RestTemplate call. Here is my code:
String auth = "admin:admin123";
byte[] encodedAuth = Base64.encodeBase64( auth.getBytes(Charset.forName("US-ASCII")) );
String authHeader = "Basic " + new String( encodedAuth );
RestTemplate restTemplate = new RestTemplate();
LinkedMultiValueMap<String, Object> files = new LinkedMultiValueMap<>();
files.add("r", "releases");
files.add("hasPom", "false");
files.add("e", "jar");
files.add("g", "com.test.proj");
files.add("a", "my-artifact");
files.add("v", "1.0.0");
files.add("p", "jar");
files.add("file", new ByteArrayResource(jarBytes));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.set("Authorization", authHeader);
HttpEntity<LinkedMultiValueMap<String, Object>> entity = new HttpEntity<>(files, headers);
String response = restTemplate.postForObject("http://localhost:8081/nexus/service/local/artifact/maven/content", entity, String.class);
This fails with a 400 Bad Request. After looking at the source, it looks like my file is failing this key check:
for (FileItem fi : files) {
if (fi.isFormField()) {
// parameters are first in "nibble"
processFormField(request, uploadContext, fi);
}
else {
// a file, this means NO parameters will income anymore
// we either received all the GAVs as params, or we have a POM to work with (file1)
...
FileItem.isFormField is from Apache Commons FileUpload. Does anyone know how I could get this to succeed with my "file" that I am passing in?
In another question (FileUpload isFormField() returning true when submitting file) the answer suggests that I need a name field, or perhaps in my case, the "type" doesn't come through. If this is the case, is it possible to specify these while making a post request?

I'm thinking that this currently isn't possible with RestTemplate. I opted to use Apache's HttpComponents, which has worked wonderfully. Here is the resulting code:
public void uploadToNexus(String version, File myJar, String artifactId)
{
try(CloseableHttpClient httpClient = HttpClients.createDefault())
{
HttpPost httpPost = new HttpPost("http://admin:admin123#mydomain:8081/nexus/service/local/artifact/maven/content");
FileBody jarFileBody = new FileBody(myJar);
HttpEntity requestEntity = MultipartEntityBuilder.create()
.addPart("r", new StringBody("releases", ContentType.TEXT_PLAIN))
.addPart("hasPom", new StringBody("false", ContentType.TEXT_PLAIN))
.addPart("e", new StringBody("jar", ContentType.TEXT_PLAIN))
.addPart("g", new StringBody("com.domain.my", ContentType.TEXT_PLAIN))
.addPart("a", new StringBody("my-artifactId", ContentType.TEXT_PLAIN))
.addPart("v", new StringBody(version, ContentType.TEXT_PLAIN))
.addPart("p", new StringBody("jar", ContentType.TEXT_PLAIN))
.addPart("file", jarFileBody)
.build();
httpPost.setEntity(requestEntity);
try(CloseableHttpResponse response = httpClient.execute(httpPost))
{
logger.info("response from nexus: {}", response.toString());
}
}
catch (IOException e)
{
throw new RuntimeException("Unable to close the httpClient", e);
}
}
I needed the following maven dependencies:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.4.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.4.1</version>
</dependency>

Related

Create mock server to test on result of RestTemplate

I am not sure if it is possible to write a Test case that can mock the "http://localhost:8888/setup" site, so the above code can hit it and I want to check if the "http://localhost:8888/setup" received the inputStream correctly.
InputStream inputStream = //got the inputStream;
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setBufferRequestBody(false);
restTemplate.setRequestFactory(requestFactory);
InputStreamResource inputStreamResource = new InputStreamResource(inputStream){
#Override
public String getFilename(){
return filename;
}
#Override
public long contentLength(){
return -1;
}
}
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>():
body.add("file", inputStreamResource);
HttpHeader headers = new HttpHeader();
headers.setContentType(MediaType.MULTIPART_FORM_DATA)LinkedMultiValueMap
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
String url = "http://localhost:8888/setup";
restTemplate.postForObject(url, requestEntity, String.class);
Try using Wiremock!
Many ways of using it, back then when I used it, I used to run a JAR (wiremock jar) and it spawns up a program on your localhost with your port specified. Henceforth, you can test by hitting that localhost on the port it's up!
For reference check this out :
https://www.softwaretestinghelp.com/wiremock-tutorial/
https://www.baeldung.com/introduction-to-wiremock
https://github.com/wiremock/wiremock

How to work with Patch method in Webclient?

I have been searching a example for patch method in Webclient. I have a four fields in my Model class, I need to update one field (i.e status field), Hence I have decided to use Patch method. But I don't any examples in internet.
I have a piece of code in RestTemplate, here I need it in Webclient, as I'm migrating to Webclient. How to achieve below code?
public void updateProfile(UpdateProfile profile, String uniqueId) {
HttpHeaders headers = new HttpHeaders();
MediaType mediaType = new MediaType("application", "merge-patch+json");
headers.setContentType(mediaType);
HttpEntity<UpdateProfile> entity = new HttpEntity<>(profile, headers);
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
RestTemplate restTemplate = new RestTemplate(requestFactory);
restTemplate.exchange(firebaseUrl+"/"+path+"/" + uniqueId + ".json",
HttpMethod.PATCH, entity, Void.class);
}
You can use the following patch method sample from the web client, i have not tested but i hope so you can use the similar approach.
WebClient webClient = WebClient.create(firebaseUrl);
webClient.patch()
.uri("+path+" + uniqueId + ".json")
.contentType(MediaType.valueOf("application/json-patch+json"))
.bodyValue(data)
.exchange();
remember to add the below artifact in your pom.xml,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

How can Spring RestTemplate deal with response content-type "text/csv; charset=utf-8-sig" sent by a third party service?

With RestTemplate, I post a request to a third party service whose response content-type is text/csv; charset=utf-8-sig
When parsing the response, Spring rejects it with the following message :
Invalid mime type "text/csv; charset=utf-8-sig": unsupported charset 'utf-8-sig'
I don't know what utf-8-sig means, but I have no other choices but adapt to the third party service.
Below is the code I use :
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, String> fileMap = new LinkedMultiValueMap<>();
ContentDisposition contentDisposition = ContentDisposition
.builder("form-data").name("data")
.filename(fichierInput.get().getName()).build();
fileMap.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition.toString());
HttpEntity<byte[]> fileEntity =
new HttpEntity<>(content.getBytes(), fileMap);
MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
body.add("data", fileEntity);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
try {
ResponseEntity<byte[]> response = restTemplate
.exchange(this.applicationProperties.getGeoloc().getUrl(),
HttpMethod.POST, requestEntity, byte[].class);
log.debug("retour de geoloc : {} ", response.getBody());
} catch (HttpClientErrorException e) {
e.printStackTrace();
}
My question is : Do I have a way to tell Spring not to read this header but instead take the request body "as-is" ?
Thanks for your help!
I ended up by creating a custom interceptor to modify the headers of the response => example here
Added this in the header:
headerMap.put(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded");
Also make sure you have latest jackson core included, If you are using gradle use:
compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.11.0.rc1'

TestRestTemplate.postForEntity returns 400 with No Descriptive Message

I'm having some trouble using TestRestTemplate.postForEntity(). My code is:
#Test
public void testAdd() {
Map<String,String> mParams=new HashMap<>();
mParams.put("joke","This is my joke");
mParams.put("description","Totally Tasteless Joke");
mParams.put("date",Long.toString(System.currentTimeMillis()));
ResponseEntity<Joke> reJoke = restTemplate.postForEntity(getURLBase()+"/add",
null,
Joke.class,
mParams
);
Joke j = reJoke.getBody();
System.out.println("Status="+reJoke.getStatusCode()+" j.getJoke()="+j.getJoke()+" id="+j.getId());
}
The returned Status value is 400. Nothing is printed on the console.
I have a testGet() that does work:
#Test
public void testGet() {
System.out.println("testGet()");
initData();
ResponseEntity<Joke> reJoke=restTemplate.getForEntity(getURLBase()+"/1",Joke.class,new HashMap<String,String>());
Joke j=reJoke.getBody();
System.out.println("Status=" + reJoke.getStatusCode()+" j =" + j + (j == null ? "" : j.getJoke()));
}
I noticed in the Javadoc for TestRestTemplate it says:
If Apache Http Client 4.3.2 or better is available (recommended) it will be used as the client, and by default configured to ignore cookies and redirects.
I've added
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.5</version>
<scope>test</scope>
</dependency>
to pom.xml, but it doesn't seem to make any difference.
Can anyone tell me how to either solve the issue, or get more information than "400"? It's really appreciated.
After a little searching, I found that the mParams was actually URL parameters. I needed to send the form encoded parameters as the request object. The working code is:
#Test
public void testAdd() {
MultiValueMap<String, String> mParams= new LinkedMultiValueMap<String, String>();
mParams.add("joke", "This is my joke");
mParams.add("description", "Totally Tasteless Joke");
mParams.add("date", Long.toString(System.currentTimeMillis()));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(mParams, headers);
ResponseEntity<Joke> reJoke = restTemplate.postForEntity(getURLBase()+"/add",
request,
Joke.class
);
Joke j = reJoke.getBody();
System.out.println("Status="+reJoke.getStatusCode()+" j.getJoke()="+j.getJoke()+" id="+j.getId());
}

Spring Rest Template to send JsonArray

I am using spring rest template to send json array as request. Source code to send request is as follow:
JSONArray jsonArray = new JSONArray();
for (Iterator iterator = itemlist.iterator(); iterator.hasNext();) {
Item item = (Item)iterator.next();
JSONObject formDetailsJson = new JSONObject();
formDetailsJson.put("id", item.getItemConfId());
formDetailsJson.put("name", item.getItems().getItemName());
formDetailsJson.put("price", item.getPrice());
formDetailsJson.put("Cost",item.getCost());
jsonArray.put(formDetailsJson);
}
List<MediaType> acceptableMediaTypes = new ArrayList<MediaType>();
acceptableMediaTypes.add(MediaType.APPLICATION_JSON);
// Prepare header
HttpHeaders headers = new HttpHeaders();
headers.setAccept(acceptableMediaTypes);
// Pass the new person and header
HttpEntity<JSONArray> entity = new HttpEntity<JSONArray>(jsonArray, headers);
System.out.println("Json Object : "+entity);
// Send the request as POST
try {
ResponseEntity<String> result = restTemplate.exchange("my url", HttpMethod.POST, entity, String.class);
} catch (Exception e) {
logger.error(e);
return "Connection not avilable please try again";
}
And to accept request:
#RequestMapping(value = "/testStock", method = RequestMethod.POST,headers="Accept=application/xml, application/json")
public #ResponseBody int testStock(#RequestBody List<ItemList> jsonArray) {
logger.debug("Received request to connect ms access : "+jsonArray.size());
//int returnSizecount = stockList.getStocklst().size();
return 1;
}
The problem is that it giving me following error:
Could not write request: no suitable HttpMessageConverter found for request type [org.json.JSONArray].Any suggestion is greatly acceptable.
There are no MessageConverter for JSONArray, so I suggest do the following.
HttpEntity<JSONArray> entity = new HttpEntity<JSONArray>(jsonArray, headers);
Convert Class JSONArray to String, and add that to HttpEntity, you know use toString
java.lang.String toString()
Make a JSON text of this JSONArray.
HttpEntity entity = new HttpEntity(jsonArray.toString(), headers);
Or change to Jackson implementation Spring have support to that. XD
If you dont want to do the above, consider create your own implementation of messageConverter, that will work but is harder
update
HttpHeaders headers = new HttpHeaders();
headers.setAccept(acceptableMediaTypes);
headers.setContentType(MediaType.APPLICATION_JSON);
update 2 Change endpoint to.
#RequestMapping(value = "/testStock", method = RequestMethod.POST)
public #ResponseBody int testStock(#RequestBody String jsonArray) {
you need to have httpmessageconverter configured for your resttemplate, please read my post for configuring http message conveter for you webservice
http://stackoverflow.com/questions/19963127/new-to-spring-and-jackson-2-what-does-this-bean-declaration-allow-for-in-a-spri/19973636#19973636.
and for you problem to convert your http request to json you might add this entry in your restemplate configuration
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
The error is quite straightforward. You do not have a converter for the JSONArray. Converting the array to a String (using toString) did help you here, but there is a better way:
Just add a converter for the json.org objects:
Add this to your pom.xml
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-json-org</artifactId>
</dependency>
And then on your ObjectMapper add the JsonOrgModule:
mapper.registerModule(new JsonOrgModule());

Resources