How to resolve a custom option in a protocol buffer FileDescriptor - java

I'm using protocol buffers 2.5 with Java. I have a proto file that defines a custom option. Another proto file uses that custom option. If I persist the corresponding FileDescriptorProto's and then read them and convert them to FileDescriptors, the reference to the custom option is manifested as unknown field. How do I cause that custom option to be resolved correctly?
Here's the code. I have two .proto files. protobuf-options.proto looks like this:
package options;
import "google/protobuf/descriptor.proto";
option java_package = "com.example.proto";
option java_outer_classname = "Options";
extend google.protobuf.FieldOptions {
optional bool scrub = 50000;
}
The imported google/protobuf/descriptor.proto is exactly the descriptor.proto that ships with Protocol Buffers 2.5.
example.proto looks like this:
package example;
option java_package = "com.example.protos";
option java_outer_classname = "ExampleProtos";
option optimize_for = SPEED;
option java_generic_services = false;
import "protobuf-options.proto";
message M {
optional int32 field1 = 1;
optional string field2 = 2 [(options.scrub) = true];
}
As you can see, field2 references the custom option defined by protobuf-options.proto.
The following code writes a binary-encoded version of all three protos to /tmp:
package com.example;
import com.google.protobuf.ByteString;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.example.protos.ExampleProtos;
import java.io.FileOutputStream;
import java.io.OutputStream;
/**
*
*/
public class PersistFDs {
public void persist(final FileDescriptor fileDescriptor) throws Exception {
System.out.println("persisting "+fileDescriptor.getName());
try (final OutputStream outputStream = new FileOutputStream("/tmp/"+fileDescriptor.getName())) {
final FileDescriptorProto fileDescriptorProto = fileDescriptor.toProto();
final ByteString byteString = fileDescriptorProto.toByteString();
byteString.writeTo(outputStream);
}
for (final FileDescriptor dependency : fileDescriptor.getDependencies()) {
persist(dependency);
}
}
public static void main(String[] args) throws Exception {
final PersistFDs self = new PersistFDs();
self.persist(ExampleProtos.getDescriptor());
}
}
Finally, the following code loads those those protos from /tmp, converts them back into FileDescriptors, and then checks for the custom option on field2:
package com.example;
import com.google.protobuf.ByteString;
import com.google.protobuf.DescriptorProtos.FileDescriptorProto;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.UnknownFieldSet.Field;
import java.io.FileInputStream;
import java.io.InputStream;
/**
*
*/
public class LoadFDs {
public FileDescriptorProto loadProto(final String filePath) throws Exception {
try (final InputStream inputStream = new FileInputStream(filePath)) {
final ByteString byteString = ByteString.readFrom(inputStream);
final FileDescriptorProto result = FileDescriptorProto.parseFrom(byteString);
return result;
}
}
public static void main(final String[] args) throws Exception {
final LoadFDs self = new LoadFDs();
final FileDescriptorProto descriptorFDProto = self.loadProto("/tmp/google/protobuf/descriptor.proto");
final FileDescriptorProto optionsFDProto = self.loadProto("/tmp/protobuf-options.proto");
final FileDescriptorProto fakeBoxcarFDProto = self.loadProto("/tmp/example.proto");
final FileDescriptor fD = FileDescriptor.buildFrom(descriptorFDProto, new FileDescriptor[0]);
final FileDescriptor optionsFD = FileDescriptor.buildFrom(optionsFDProto, new FileDescriptor[] { fD });
final FileDescriptor fakeBoxcarFD = FileDescriptor.buildFrom(fakeBoxcarFDProto, new FileDescriptor[] { optionsFD });
final FieldDescriptor optionsFieldDescriptor = optionsFD.findExtensionByName("scrub");
if (optionsFieldDescriptor == null) {
System.out.println("Did not find scrub's FieldDescriptor");
System.exit(1);
}
final FieldDescriptor sFieldDescriptor = fakeBoxcarFD.findMessageTypeByName("M").findFieldByName("field2");
System.out.println("unknown option fields "+sFieldDescriptor.getOptions().getUnknownFields());
final boolean hasScrubOption = sFieldDescriptor.getOptions().hasField(optionsFieldDescriptor);
System.out.println("hasScrubOption: "+hasScrubOption);
}
}
When I run LoadFDs, it fails with this exception:
unknown option fields 50000: 1
Exception in thread "main" java.lang.IllegalArgumentException: FieldDescriptor does not match message type.
at com.google.protobuf.GeneratedMessage$ExtendableMessage.verifyContainingType(GeneratedMessage.java:812)
at com.google.protobuf.GeneratedMessage$ExtendableMessage.hasField(GeneratedMessage.java:761)
at com.example.LoadFDs.main(LoadFDs.java:42)
The options for FieldDescriptor for the s field ought to have a field for that custom option, but instead it has an unknown field. The field number and value on the unknown field are correct. It's just that the custom option is not getting resolved. How do I fix that?

You need to use FileDescriptorProto.parseFrom(byte[] data, ExtensionRegistryLite extensionRegistry, and explicitly create an ExtentionRegistry. Here's one way to create an extension registry:
ExtensionRegistry extensionRegistry = ExtensionRegistry.newInstance();
com.example.proto.Options.registerAllExtensions(extensionRegistry);
(where com.example.proto.Options is the compiled custom options class)
This obviously only works if you have access to the custom options compiled file on client side. I don't know if there's a way to serialize the extension and deserialize it on the client side.

Related

OpenAPI throws Could not resolve reference: Could not resolve pointer: for #ExampleObject files

I am developing a Quarkus service-based application for which I am adding open API based annotations such as #ExampleObject. For this, I would like to add the resources file contents as an example that can appear in the SwaggerUI.
I am getting the following error when I add the reference to the files from the resources folder:
Errors
Resolver error at paths./api/generateTestData.post.requestBody.content.application/json.examples.Example1 Schema.$ref
Could not resolve reference: Could not resolve pointer: /Example1.json does not exist in document
Resolver error at paths./api/generateTestData.post.requestBody.content.application/json.examples.Example2 Schema.$ref
Could not resolve reference: Could not resolve pointer: /Example2.json does not exist in document
Following is my Quarkus based Java code:
#RequestBody(description = "InputTemplate body",
content = #Content(schema = #Schema(implementation = InputTemplate.class), examples = {
#ExampleObject(name = "Example-1",
description = "Example-1 for InputTemplate.",
ref = "#/resources/Example1.json"), externalValue = "#/resources/Example2.json"
#ExampleObject(name = "Example-2",
description = "Example-2 for InputTemplate.",
ref = "#/resources/Example1.json") //externalValue = "#/resources/Example1.json"
}))
Note:
I am able to add the String as value but the content for these examples is very large so I would like to read from the files only so trying this approach.
Is there any way I can access the resources file and add it as a ref within my #ExampleObject
A working example below:
Create an OASModelFilter class which implements OASFilter:
package org.acme;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.eclipse.microprofile.openapi.OASFactory;
import org.eclipse.microprofile.openapi.OASFilter;
import org.eclipse.microprofile.openapi.models.Components;
import org.eclipse.microprofile.openapi.models.OpenAPI;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import org.eclipse.microprofile.openapi.models.examples.Example;
public class OASModelFilter implements OASFilter {
ObjectMapper objectMapper = new ObjectMapper();
#Override
public void filterOpenAPI(OpenAPI openAPI) {
//openApi.getComponents() will result in NULL as we don't have any openapi.yaml file.
Components defaultComponents = OASFactory.createComponents();
if(openAPI.getComponents() == null){
openAPI.setComponents(defaultComponents);
}
generateExamples().forEach(openAPI.getComponents()::addExample);
}
Map<String, Example> generateExamples() {
Map<String, Example> examples = new LinkedHashMap<>();
try {
//loop over your Example JSON Files,..
//In this case, the example is only for 1 file.
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream userJsonFileInputStream = loader.getResourceAsStream("user.json");
String fileJSONContents = new String(userJsonFileInputStream.readAllBytes(), StandardCharsets.UTF_8);
//Create a unique example for each File/JSON
Example createExample = OASFactory.createExample()
.description("User JSON Description")
.value(objectMapper.readValue(fileJSONContents, ObjectNode.class));
// Save your Example with a Unique Map Key.
examples.put("createExample", createExample);
} catch (IOException ioException) {
System.out.println("An error occured" + ioException);
}
return examples;
}
}
The controller using createExample as its #ExampleObject.
#Path("/hello")
public class GreetingResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
#APIResponses(
value = {
#APIResponse(responseCode = "200", content = #Content(
mediaType = "*/*",
examples = {
#ExampleObject(name = "boo",
summary = "example of boo",
ref = "createExample")
}
))
}
)
public String hello() {
return "Hello RESTEasy";
}
}
In your application.properties, specify the following: Take note that it references the full package path of the Filter.
mp.openapi.filter=org.acme.OASModelFilter
Contents of user.json file:
{
"hello": "world",
"my": "json",
"testing": "manually adding resource JSONs as examples"
}
The JSON file used is located directly under resources. Of course you can change that path, but you need to update your InputStream.
mvn clean install
mvn quarkus:dev
Go to http://localhost:8080/q/swagger-ui/ and you will now see your user.json file contents displayed
Hopes this helps you,
References for my investigation:
https://github.com/labcabrera/rolemaster-core/blob/c68331c10ef358f6288518350c79d4868ff60d2c/src/main/java/org/labcabrera/rolemaster/core/config/OpenapiExamplesConfig.java
https://github.com/bf2fc6cc711aee1a0c2a/kafka-admin-api/blob/54496dd67edc39a81fa7c6da4c966560060c7e3e/kafka-admin/src/main/java/org/bf2/admin/kafka/admin/handlers/OASModelFilter.java
The below works, but as you can see I am creating the PATHS, and you still need to know what the (path/address/is) in order to create paths.
It could help you in thinking in approaching it in a different way.
If you are considering modifying the #ApiResponses/#ApiResponse annotations directly, then it wont work.
package org.acme;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.eclipse.microprofile.openapi.OASFactory;
import org.eclipse.microprofile.openapi.OASFilter;
import org.eclipse.microprofile.openapi.models.Components;
import org.eclipse.microprofile.openapi.models.OpenAPI;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;
import org.eclipse.microprofile.openapi.models.examples.Example;
import io.quarkus.logging.Log;
public class CustomOASFilter implements OASFilter {
ObjectMapper objectMapper = new ObjectMapper();
#Override
public void filterOpenAPI(OpenAPI openAPI) {
//openApi.getComponents() will result in NULL as we don't have any openapi.yaml file.
Components defaultComponents = OASFactory.createComponents();
if (openAPI.getComponents() == null) {
openAPI.setComponents(defaultComponents);
}
generateExamples().forEach(openAPI.getComponents()::addExample);
openAPI.setPaths(OASFactory.createPaths()
.addPathItem(
"/hello/customer", OASFactory.createPathItem()
.GET(
OASFactory.createOperation()
.operationId("hello-customer-get")
.summary("A simple get call")
.description("Getting customer information")
.responses(
OASFactory.createAPIResponses()
.addAPIResponse(
"200", OASFactory.createAPIResponse()
.content(OASFactory.createContent()
.addMediaType("application/json", OASFactory.createMediaType()
.examples(generateExamples()))))))));
}
Map<String, Example> generateExamples() {
Map<String, Example> examples = new LinkedHashMap<>();
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
String userJSON = new String(loader.getResourceAsStream("user.json").readAllBytes(), StandardCharsets.UTF_8);
String customerJson = new String(loader.getResourceAsStream("customer.json").readAllBytes(), StandardCharsets.UTF_8);
Example userExample = OASFactory.createExample()
.description("User JSON Example Description")
.value(objectMapper.readValue(userJSON, ObjectNode.class));
Example customerExample = OASFactory.createExample()
.description("Customer JSON Example Description")
.value(objectMapper.readValue(customerJson, ObjectNode.class));
examples.put("userExample", userExample);
examples.put("customerExample", customerExample);
} catch (IOException ioException) {
Log.error(ioException);
}
return examples;
}
}
EDIT: This is working well in spring-boot
The above answer might work but it has too much code to put into to make it work.
Instead, you can use externalValue field to pass on the JSON file.
For example,
#ExampleObject(
summary = "temp",
name =
"A 500 error",
externalValue = "/response.json"
)
And now you can create your json file under /resources/static like below,
Swagger doc screenshot
And that's all you need. You don't need to write any manual code here.
Hope this will help you fix the issue.

java.lang.ClassNotFoundException, PATH is corrent

I'm trying to create a simple program, but of course JAVA thinks otherwise: it's not that simple.
I need to dynamically instantiate a class, meaning that the user gives a class name from keyboard, and then an object of the class type with that name is created.
Code:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.*;
public class NimMain {
public static void main(String[] args) throws IOException {
BufferedReader Olvaso = new BufferedReader(new InputStreamReader(System.in));
String be = Olvaso.readLine();
String[] kapcsolo = be.split(" ");
switch (kapcsolo[0]) {
case "uj": uj(kapcsolo);
case "lep":
case "listaz":
case "ment":
case "tolt":
}}
public static void uj(String[] s) {
try {
int b = 2;
String nev = s[1];
Class NimJatek = Class.forName(nev);
Constructor con = NimJatek.getConstructor(String[].class, int.class);
Object xyz = con.newInstance(s,b);
} catch (Exception e) {
e.printStackTrace();
}
}
}
The class which fails to instantiate is NimJatek, which is in the same directory, in the same (unnamed) package.
When I try to run this program, it gives the java.lang.ClassNotFoundException error.
I think you baffle yourself by your not very clear constructs. At the end you use the second value of your input string to be loaded as class. If you provide the correct string there it will be laoded correctly. The input string:
"uj NimJatek"
will lead to a correctly found class NimJatek - provided NimJatek is in your root package AND this root package is on your classpath.

Jackrabbit WebDAV Synchronization Examples?

I'm using the Jackrabbit library for communicating with a cloud storage using the webdav protocol. I need a way to list all files from a specific directory and get the last modified property but I can't seem to find any working examples on this.
I basically need code to synchronize files from the local directory with the webdav url.
import java.io.File;
import java.io.FileInputStream;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.jackrabbit.webdav.client.methods.DavMethod;
import org.apache.jackrabbit.webdav.client.methods.MkColMethod;
import org.apache.jackrabbit.webdav.client.methods.PutMethod;
public class WebDavClient
{
private String resourceUrl;
private HttpClient client;
private Credentials credentials;
private DavMethod method;
public WebDavClient(String resourceUrl, String username, String password)
throws Exception
{
this.resourceUrl = resourceUrl;
client = new HttpClient();
credentials = new UsernamePasswordCredentials(username, password);
client.getState().setCredentials(AuthScope.ANY, credentials);
}
public int upload(String fileToUpload) throws Exception
{
method = new PutMethod(getUpdatedWebDavPath(fileToUpload));
RequestEntity requestEntity = new InputStreamRequestEntity(
new FileInputStream(fileToUpload));
((PutMethod) method).setRequestEntity(requestEntity);
client.executeMethod(method);
return method.getStatusCode();
}
public int createFolder(String folder) throws Exception
{
method = new MkColMethod(getUpdatedWebDavPath(folder));
client.executeMethod(method);
return method.getStatusCode();
}
private String getUpdatedWebDavPath(String file)
{
// Make sure file names do not contain spaces
return resourceUrl + "/" + new File(file).getName().replace(" ", "");
}
}
Usage example for uploading the file Test.txt to the Backup folder:
String myAccountName = "...";
String myPassword = "...";
WebDavClient webdavUploader = new WebDavClient("https:\\\\webdav.hidrive.strato.com\\users\\" + myAccountName + "\\Backup", myAccountName, myPassword);
webdavUploader.upload("C:\\Users\\Username\\Desktop\\Test.txt");
Here's a list of different DavMethods that could be helpful:
http://jackrabbit.apache.org/api/1.6/org/apache/jackrabbit/webdav/client/methods/package-summary.html
Please help, I'm been struggling on this for so long!
Take a look at the AMES WebDAV Client code from Krusche and Partner on the EU portal. It is licensed under GPL, so should it may fit your purpose.
https://joinup.ec.europa.eu/svn/ames-web-service/trunk/AMES-WebDAV/ames-webdav/src/de/kp/ames/webdav/WebDAVClient.java
It works for me, though to access e.g. Win32LastModifiedTime I need to get the custom namespace, e.g.
private static final Namespace WIN32_NAMESPACE = Namespace.getNamespace("Z2", "urn:schemas-microsoft-com:");
and retrieve the custom Property Win32LastModifiedTime from the properties.
/*
* Win32LastModifiedTime
*/
String win32lastmodifiedtime = null;
DavProperty<?> Win32LastModifiedTime = properties.get("Win32LastModifiedTime", WIN32_NAMESPACE);
if ((Win32LastModifiedTime != null) && (Win32LastModifiedTime.getValue() != null)) win32lastmodifiedtime = Win32LastModifiedTime.getValue().toString();

jAudio Feature Extractor : Null Exception

My project is to create an Android app that can perform feature extraction and classification of audio files. So first, I'm creating a Java application as a test run.
I'm attempting to use jAudio's feature extractor package to extract audio features from an audio file.
As a starter, I want to input a .wav file and run the feature extraction operation upon that file, and then store the results as an .ARFF file.
However, I'm getting the below NullPointer Exception error from a package within the project:
Exception in thread "main" java.lang.NullPointerException
at java.io.DataOutputStream.writeBytes(Unknown Source)
at jAudioFeatureExtractor.jAudioTools.FeatureProcessor.writeValuesARFFHeader(FeatureProcessor.java:853)
at jAudioFeatureExtractor.jAudioTools.FeatureProcessor.<init>(FeatureProcessor.java:258)
at jAudioFeatureExtractor.DataModel.extract(DataModel.java:308)
at Mfccarffwriter.main(Mfccarffwriter.java:70)
Initially I thought it was a file permission issue(i.e, the program was not being allowed to write a file because of lack of permissions), but even after granting every kind of permission to Eclipse 4.2.2(I'm running Windows 7, 64 bit version), I'm still getting the NullException bug.
The package code where the offending exception originates from is given below:
/**
* Write headers for an ARFF file. If saving for overall features, this must
* be postponed until the overall features have been calculated. If this a
* perWindow arff file, then all the feature headers can be extracted now
* and no hacks are needed.
* <p>
* <b>NOTE</b>: This procedure breaks if a feature to be saved has a
* variable number of dimensions
*
* #throws Exception
*/
private void writeValuesARFFHeader() throws Exception {
String sep = System.getProperty("line.separator");
String feature_value_header = "#relation jAudio" + sep;
values_writer.writeBytes(feature_value_header); // exception here
if (save_features_for_each_window && !save_overall_recording_features) {
for (int i = 0; i < feature_extractors.length; ++i) {
if (features_to_save[i]) {
String name = feature_extractors[i].getFeatureDefinition().name;
int dimension = feature_extractors[i]
.getFeatureDefinition().dimensions;
for (int j = 0; j < dimension; ++j) {
values_writer.writeBytes("#ATTRIBUTE \"" + name + j
+ "\" NUMERIC" + sep);
}
}
}
values_writer.writeBytes(sep);
values_writer.writeBytes("#DATA" + sep);
}
}
Here's the main application code:
import java.io.*;
import java.util.Arrays;
import com.sun.xml.internal.bind.v2.runtime.RuntimeUtil.ToStringAdapter;
import jAudioFeatureExtractor.Cancel;
import jAudioFeatureExtractor.DataModel;
import jAudioFeatureExtractor.Updater;
import jAudioFeatureExtractor.Aggregators.AggregatorContainer;
import jAudioFeatureExtractor.AudioFeatures.FeatureExtractor;
import jAudioFeatureExtractor.AudioFeatures.MFCC;
import jAudioFeatureExtractor.DataTypes.RecordingInfo;
import jAudioFeatureExtractor.jAudioTools.*;
public static void main(String[] args) throws Exception {
// Display information about the wav file
File extractedFiletoTest = new File("./microwave1.wav");
String randomID = Integer.toString((int) Math.random());
String file_path = "E:/Weka-3-6/tmp/microwave1.wav";
AudioSamples sampledExampleFile = new AudioSamples(extractedFiletoTest,randomID,false);
RecordingInfo[] samplefileInfo = new RecordingInfo[5];
samplefileInfo[1] = new RecordingInfo(randomID, file_path, sampledExampleFile, true);
double samplingrate= sampledExampleFile.getSamplingRateAsDouble();
int windowsize= 4096;
boolean normalize = false;
OutputStream valsavepath = new FileOutputStream(".\\values");
OutputStream defsavepath = new FileOutputStream(".\\definitions");
boolean[] featurestosaveamongall = new boolean[10];
Arrays.fill(featurestosaveamongall, Boolean.TRUE);
double windowoverlap = 0.0;
DataModel mfccDM = new DataModel("features.xml",null);
mfccDM.extract(windowsize, 0.5, samplingrate, true, true, false, samplefileInfo, 1); /// invokes the writeValuesARFFHeader function.
}
}
You can download the whole project (done so far)here.
This may be a bit late but I had the same issue and I tracked it down to the featureKey and featureValue never being set in the DataModel. There is not a set method for these but they are public field. Here is my code:
package Sound;
import jAudioFeatureExtractor.ACE.DataTypes.Batch;
import jAudioFeatureExtractor.DataModel;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class Analysis {
private static String musicFile = "/home/chris/IdeaProjects/AnotherProj/res/SheMovesInHerOwnWay15s.wav";
private static String featureFile = "/home/chris/IdeaProjects/AnotherProj/res/features.xml";
private static String settingsFile = "/home/chris/IdeaProjects/AnotherProj/res/settings.xml";
private static String FKOuputFile = "/home/chris/IdeaProjects/AnotherProj/res/fk.xml";
private static String FVOuputFile = "/home/chris/IdeaProjects/AnotherProj/res/fv.xml";
public static void main(String[] args){
Batch batch = new Batch(featureFile, null);
try{
batch.setRecordings(new File[]{new File(musicFile)});
batch.getAggregator();
batch.setSettings(settingsFile);
DataModel dm = batch.getDataModel();
OutputStream valsavepath = new FileOutputStream(FVOuputFile);
OutputStream defsavepath = new FileOutputStream(FKOuputFile);
dm.featureKey = defsavepath;
dm.featureValue = valsavepath;
batch.setDataModel(dm);
batch.execute();
}
catch (Exception e){
e.printStackTrace();
}
}
}
I created the settings.xml file using the GUI and just copied the features.xml file from the directory where you saved the jar.
Hope this helps

Amazon Product Advertising API through Java/SOAP

I have been playing with Amazon's Product Advertising API, and I cannot get a request to go through and give me data. I have been working off of this: http://docs.amazonwebservices.com/AWSECommerceService/2011-08-01/GSG/ and this: Amazon Product Advertising API signed request with Java
Here is my code. I generated the SOAP bindings using this: http://docs.amazonwebservices.com/AWSECommerceService/2011-08-01/GSG/YourDevelopmentEnvironment.html#Java
On the Classpath, I only have: commons-codec.1.5.jar
import com.ECS.client.jax.AWSECommerceService;
import com.ECS.client.jax.AWSECommerceServicePortType;
import com.ECS.client.jax.Item;
import com.ECS.client.jax.ItemLookup;
import com.ECS.client.jax.ItemLookupRequest;
import com.ECS.client.jax.ItemLookupResponse;
import com.ECS.client.jax.ItemSearchResponse;
import com.ECS.client.jax.Items;
public class Client {
public static void main(String[] args) {
String secretKey = <my-secret-key>;
String awsKey = <my-aws-key>;
System.out.println("API Test started");
AWSECommerceService service = new AWSECommerceService();
service.setHandlerResolver(new AwsHandlerResolver(
secretKey)); // important
AWSECommerceServicePortType port = service.getAWSECommerceServicePort();
// Get the operation object:
com.ECS.client.jax.ItemSearchRequest itemRequest = new com.ECS.client.jax.ItemSearchRequest();
// Fill in the request object:
itemRequest.setSearchIndex("Books");
itemRequest.setKeywords("Star Wars");
// itemRequest.setVersion("2011-08-01");
com.ECS.client.jax.ItemSearch ItemElement = new com.ECS.client.jax.ItemSearch();
ItemElement.setAWSAccessKeyId(awsKey);
ItemElement.getRequest().add(itemRequest);
// Call the Web service operation and store the response
// in the response object:
com.ECS.client.jax.ItemSearchResponse response = port
.itemSearch(ItemElement);
String r = response.toString();
System.out.println("response: " + r);
for (Items itemList : response.getItems()) {
System.out.println(itemList);
for (Item item : itemList.getItem()) {
System.out.println(item);
}
}
System.out.println("API Test stopped");
}
}
Here is what I get back.. I was hoping to see some Star Wars books available on Amazon dumped out to my console :-/:
API Test started
response: com.ECS.client.jax.ItemSearchResponse#7a6769ea
com.ECS.client.jax.Items#1b5ac06e
API Test stopped
What am I doing wrong (Note that no "item" in the second for loop is being printed out, because its empty)? How can I troubleshoot this or get relevant error information?
I don't use the SOAP API but your Bounty requirements didn't state that it had to use SOAP only that you wanted to call Amazon and get results. So, I'll post this working example using the REST API which will at least fulfill your stated requirements:
I would like some working example code that hits the amazon server and returns results
You'll need to download the following to fulfill the signature requirements:
http://associates-amazon.s3.amazonaws.com/signed-requests/samples/amazon-product-advt-api-sample-java-query.zip
Unzip it and grab the com.amazon.advertising.api.sample.SignedRequestsHelper.java file and put it directly into your project. This code is used to sign the request.
You'll also need to download Apache Commons Codec 1.3 from the following and add it to your classpath i.e. add it to your project's library. Note that this is the only version of Codec that will work with the above class (SignedRequestsHelper)
http://archive.apache.org/dist/commons/codec/binaries/commons-codec-1.3.zip
Now you can copy and paste the following making sure to replace your.pkg.here with the proper package name and replace the SECRET and the KEY properties:
package your.pkg.here;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class Main {
private static final String SECRET_KEY = "<YOUR_SECRET_KEY>";
private static final String AWS_KEY = "<YOUR_KEY>";
public static void main(String[] args) {
SignedRequestsHelper helper = SignedRequestsHelper.getInstance("ecs.amazonaws.com", AWS_KEY, SECRET_KEY);
Map<String, String> params = new HashMap<String, String>();
params.put("Service", "AWSECommerceService");
params.put("Version", "2009-03-31");
params.put("Operation", "ItemLookup");
params.put("ItemId", "1451648537");
params.put("ResponseGroup", "Large");
String url = helper.sign(params);
try {
Document response = getResponse(url);
printResponse(response);
} catch (Exception ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
}
}
private static Document getResponse(String url) throws ParserConfigurationException, IOException, SAXException {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document doc = builder.parse(url);
return doc;
}
private static void printResponse(Document doc) throws TransformerException, FileNotFoundException {
Transformer trans = TransformerFactory.newInstance().newTransformer();
Properties props = new Properties();
props.put(OutputKeys.INDENT, "yes");
trans.setOutputProperties(props);
StreamResult res = new StreamResult(new StringWriter());
DOMSource src = new DOMSource(doc);
trans.transform(src, res);
String toString = res.getWriter().toString();
System.out.println(toString);
}
}
As you can see this is much simpler to setup and use than the SOAP API. If you don't have a specific requirement for using the SOAP API then I would highly recommend that you use the REST API instead.
One of the drawbacks of using the REST API is that the results aren't unmarshaled into objects for you. This could be remedied by creating the required classes based on the wsdl.
This ended up working (I had to add my associateTag to the request):
public class Client {
public static void main(String[] args) {
String secretKey = "<MY_SECRET_KEY>";
String awsKey = "<MY AWS KEY>";
System.out.println("API Test started");
AWSECommerceService service = new AWSECommerceService();
service.setHandlerResolver(new AwsHandlerResolver(secretKey)); // important
AWSECommerceServicePortType port = service.getAWSECommerceServicePort();
// Get the operation object:
com.ECS.client.jax.ItemSearchRequest itemRequest = new com.ECS.client.jax.ItemSearchRequest();
// Fill in the request object:
itemRequest.setSearchIndex("Books");
itemRequest.setKeywords("Star Wars");
itemRequest.getResponseGroup().add("Large");
// itemRequest.getResponseGroup().add("Images");
// itemRequest.setVersion("2011-08-01");
com.ECS.client.jax.ItemSearch ItemElement = new com.ECS.client.jax.ItemSearch();
ItemElement.setAWSAccessKeyId(awsKey);
ItemElement.setAssociateTag("th0426-20");
ItemElement.getRequest().add(itemRequest);
// Call the Web service operation and store the response
// in the response object:
com.ECS.client.jax.ItemSearchResponse response = port
.itemSearch(ItemElement);
String r = response.toString();
System.out.println("response: " + r);
for (Items itemList : response.getItems()) {
System.out.println(itemList);
for (Item itemObj : itemList.getItem()) {
System.out.println(itemObj.getItemAttributes().getTitle()); // Title
System.out.println(itemObj.getDetailPageURL()); // Amazon URL
}
}
System.out.println("API Test stopped");
}
}
It looks like the response object does not override toString(), so if it contains some sort of error response, simply printing it will not tell you what the error response is. You'll need to look at the api for what fields are returned in the response object and individually print those. Either you'll get an obvious error message or you'll have to go back to their documentation to try to figure out what is wrong.
You need to call the get methods on the Item object to retrieve its details, e.g.:
for (Item item : itemList.getItem()) {
System.out.println(item.getItemAttributes().getTitle()); //Title of item
System.out.println(item.getDetailPageURL()); // Amazon URL
//etc
}
If there are any errors you can get them by calling getErrors()
if (response.getOperationRequest().getErrors() != null) {
System.out.println(response.getOperationRequest().getErrors().getError().get(0).getMessage());
}

Categories

Resources