WSO2 ESB error on deploying custom mediator - java

I'm trying to implement a custom mediator in wso2 ESB 4.8.
I used this article as a reference, and reffered to the docs as well, but couldn't get ESB to recognize my xml configuration for my mediator.
I followed all the steps mentioned in the docs, moved the mediator's project .jar to <ESB_HOME>/ repository/components/lib and restarted the server, but I keep receiving the following error during deployment:
ERROR - MediatorFactoryFinder Unknown mediator referenced by configuration element : {http://ws.apache.org/ns/synapse}currencyMediator
Here follows the mediator code:
package org.wso2.esb.tutorial.custom;
import java.util.Iterator;
import javax.xml.namespace.QName;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNode;
import org.apache.axiom.soap.SOAPBody;
import org.apache.synapse.MessageContext;
import org.apache.synapse.mediators.AbstractMediator;
public class CurrencyMedXML extends AbstractMediator {
private String symbol = "$";
public boolean mediate(MessageContext synCtx) {
// get symbol, last elements of SOAP envelope
SOAPBody body = synCtx.getEnvelope().getBody();
OMElement firstElement = body.getFirstElement();
//Iterator it = firstElement.getChildren();
Iterator it = firstElement.getChildrenWithName(new QName( "return"));
while (it.hasNext()) {
OMNode node = (OMNode)it.next();
if (node.getType()==OMNode.ELEMENT_NODE) {
OMElement ele=(OMElement)node;
String text = ele.getText();
ele.setText(symbol+text);
}
}
return true;
}
public void setSymbol(String sym){
symbol=sym;
}
public String getSymbol(){
return symbol;
}
}
The serializer implementation:
package org.wso2.esb.tutorial.custom;
import org.apache.axiom.om.OMElement;
import org.apache.synapse.Mediator;
import org.apache.synapse.config.xml.AbstractMediatorSerializer;
public class CurrencyMedXMLSerializer extends AbstractMediatorSerializer {
public String getMediatorClassName() {
return CurrencyMedXML.class.getName();
}
#Override
protected OMElement serializeSpecificMediator(Mediator m) {
if (!(m instanceof CurrencyMedXML)) {
handleException("Unsupported mediator passed in for serialization : "
+ m.getType());
}
CurrencyMedXML mediator = (CurrencyMedXML) m;
OMElement CurrencyMediatorElement = fac
.createOMElement(CurrencyMedXMLFactory.CURRENCY_MEDIATOR_Q);
saveTracingState(CurrencyMediatorElement, mediator);
OMElement symbolElement = fac.createOMElement(CurrencyMedXMLFactory.SYMBOL_Q, CurrencyMediatorElement);
symbolElement.setText(mediator.getSymbol());
return CurrencyMediatorElement;
}
}
And the Factory implementation:
package org.wso2.esb.tutorial.custom;
import java.util.Properties;
import javax.xml.namespace.QName;
import org.apache.axiom.om.OMElement;
import org.apache.synapse.Mediator;
import org.apache.synapse.SynapseException;
import org.apache.synapse.config.xml.AbstractMediatorFactory;
import org.apache.synapse.config.xml.XMLConfigConstants;
public class CurrencyMedXMLFactory extends AbstractMediatorFactory {
static final QName CURRENCY_MEDIATOR_Q = new QName(
XMLConfigConstants.SYNAPSE_NAMESPACE, "currencyMediator");
static final QName SYMBOL_Q = new QName(
XMLConfigConstants.SYNAPSE_NAMESPACE, "symbol");
public QName getTagQName() {
return CURRENCY_MEDIATOR_Q;
}
#Override
protected Mediator createSpecificMediator(OMElement elem, Properties properties) {
// create new mediator
CurrencyMedXML newMediator = new CurrencyMedXML();
// setup initial settings
processAuditStatus(newMediator, elem);
OMElement symbolElement = elem.getFirstChildWithName(SYMBOL_Q);
if (symbolElement != null) {
String symbol = symbolElement.getText();
newMediator.setSymbol(symbol);
} else {
throw new SynapseException("default percentage element missing");
}
return newMediator;
}
}
Here's a snippet from the proxy service where I reference the custom mediator:
<outSequence>
<currencyMediator>
<symbol>$</symbol>
</currencyMediator>
<send/>
</outSequence>

I haven't tried that custom tag method. But just for your information, you can call your class mediator like this too.
<class name="samples.mediators.DiscountQuoteMediator">
<property name="discountFactor" value="10"/>
<property name="bonusFor" value="5"/>
</class>

I had the same problem when I created my custom mediator with WSO2 Dev Studio. More Information can be found here.
To solve it, I had to build the mediator using maven from command line.
Hope that helps.

It seems that bundles with defined Fragment-Host synapse-core don't get registered properly. There is a workaround to do it manually.
For details look here

Related

Illegal argument exception for Optaplanner project, solver config does not exist as a classpath resource in the class loader

I am getting an error when running my SpringBoot application in my solver class. I am trying to create the solver factory from an xml resource contained in the resources folder exactly like the employee rostering example for OptaPlanner but I am getting an Illegal argument exception that I can't figure out why. I tried moving the xml file into the same folder as the solver class but the same error appears. I don't know why the exception is being thrown when I am following the example code that OptaPlanner has for employee rostering ?
Below is my code for my solver class:
package com.schedule.demo.solver;
import com.schedule.demo.domain.Roster;
import com.schedule.demo.service.RosterService;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import org.optaplanner.core.impl.score.director.ScoreDirector;
import org.optaplanner.core.impl.score.director.ScoreDirectorFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.context.annotation.ApplicationScope;
#ApplicationScope
#Component
public class ScheduleSolverManager implements ApplicationRunner {
public static final String SOLVER_CONFIG = "com/schedule/demo/service/solver/scheduleSolverConfig.xml";
protected final transient Logger logger = LoggerFactory.getLogger(getClass());
private SolverFactory<Roster> rosterSolverFactory;
private ScoreDirectorFactory<Roster> rosterScoreDirectorFactory;
private ThreadPoolTaskExecutor taskExecutor;
private ConcurrentMap<Integer, Solver<Roster>> deptIdToSolverMap = new ConcurrentHashMap<>();
private RosterService rosterService;
public ScheduleSolverManager(ThreadPoolTaskExecutor taskExecutor, RosterService rosterService){
this.taskExecutor = taskExecutor;
this.rosterService = rosterService;
}
#Override
public void run(ApplicationArguments args) throws Exception {
setupRosterSolverFactory();
}
private void setupRosterSolverFactory() {
rosterSolverFactory = SolverFactory.createFromXmlResource("com/schedule/demo/service/solver/scheduleSolverConfig.xml", ScheduleSolverManager.class.getClassLoader());
rosterScoreDirectorFactory = rosterSolverFactory.buildSolver().getScoreDirectorFactory();
}
public CountDownLatch solve(Integer deptID){
final CountDownLatch solvingEndedLatch = new CountDownLatch(1);
try{
taskExecutor.execute(()->{
Solver<Roster> rosterSolver = rosterSolverFactory.buildSolver();
deptIdToSolverMap.put(deptID, rosterSolver);
rosterSolver.addEventListener(event -> {
if(event.isEveryProblemFactChangeProcessed()){
logger.info("New best solution found for deptId ({}).", deptID);
Roster newRoster = event.getNewBestSolution();
rosterService.updateShiftsOfRoster(newRoster);
}
});
Roster roster = rosterService.buildRoster(deptID);
try {
rosterSolver.solve(roster);
solvingEndedLatch.countDown();
} finally {
deptIdToSolverMap.remove(deptID);
}
});
} catch (Throwable e) {
logger.error("Error solving for deptId (" + deptID + ").", e);
}
return solvingEndedLatch;
}
public Roster getRoster(final Integer deptId){
Solver<Roster> rosterSolver = deptIdToSolverMap.get(deptId);
return rosterSolver == null ? null : rosterSolver.getBestSolution();
}
public ScoreDirector<Roster> getScoreDirector() {
return rosterScoreDirectorFactory.buildScoreDirector();
}
}
The error when run is as follows:
Caused by: java.lang.IllegalArgumentException: The solverConfigResource (com/schedule/demo/service/solver/scheduleSolverConfig.xml) does not exist as a classpath resource in the classLoader (org.springframework.boot.devtools.restart.classloader.RestartClassLoader#2302b8ed).
Use the optaplanner-spring-boot-starter. It simplifies Spring integration greatly.
Guide: https://github.com/spring-guides/getting-started-guides/pull/126
Video: https://www.youtube.com/watch?v=U2N02ReT9CI
If you don't want to use the starter, this code might fix it: SolverFactory.createFromXmlResource("com/schedule/demo/service/solver/scheduleSolverConfig.xml", Roster.class.getClassLoader()); (uses the classloader of Roster).
You have to place the solver config XML under src/main/resources in order to be able to load it as a classpath resource.
So, if you load it using the resource name com/schedule/demo/service/solver/scheduleSolverConfig.xml, then the file has to be located at
src/main/resources/com/schedule/demo/service/solver/scheduleSolverConfig.xml

Can't get Active Objects to work in Jira

I am currently doing an internship that involves creating Jira plugins to solve some problems the users at the company experience with Jira. For the first extension I need to create a new section on the view issue page where users can put input and edit the input. In order to do this I've created a web-panel which contains a input. But no matter what I try (And I've been trying different things for the last 3 weeks) I can't get active objects to work.
Currently the moment the plugin tries to load in jira it shows the error: "Error rendering 'tig.jira.extension.tigPasswordExtension:issue-page-input'. Please contact your JIRA administrators." and when I try to test the REST API call in the restbrowser it shows the error:
""message": "AOP configuration seems to be invalid: tried calling method [public abstract net.java.ao.RawEntity[] com.atlassian.activeobjects.external.ActiveObjects.find(java.lang.Class,net.java.ao.Query)] on target [com.atlassian.activeobjects.osgi.TenantAwareActiveObjects#12bfb51b]; nested exception is java.lang.IllegalArgumentException: object is not an instance of declaring class",
_ "status-code": 500,"_
Is there anyone here that can replicate my problem and pinpoint what I am screwing up?
My current files are as follows:
Atlassian-plugin.xml
<atlassian-plugin key="${atlassian.plugin.key}" name="${project.name}" plugins-version="2">
<plugin-info>
<description>${project.description}</description>
<version>${project.version}</version>
<vendor name="${project.organization.name}" url="${project.organization.url}"/>
<param name="plugin-icon">images/pluginIcon.png</param>
<param name="plugin-logo">images/pluginLogo.png</param>
</plugin-info>
<!-- add our i18n resource -->
<resource type="i18n" name="i18n" location="tigPasswordExtension"/>
<!-- add our web resources -->
<web-resource key="tigPasswordExtension-resources" name="tigPasswordExtension Web Resources">
<dependency>com.atlassian.auiplugin:ajs</dependency>
<resource type="download" name="tigPasswordExtension.css" location="/css/tigPasswordExtension.css"/>
<resource type="download" name="tigPasswordExtension.js" location="/js/tigPasswordExtension.js"/>
<resource type="download" name="images/" location="/images"/>
<context>tigPasswordExtension.resource</context>
</web-resource>
<web-panel name="IssuePageInput" i18n-name-key="issue-page-input.name" key="issue-page-input" location="atl.jira.view.issue.right.context" weight="1">
<description key="issue-page-input.description">Passwords en SSH</description>
<context-provider class="tig.jira.extension.tigPasswordExtension.PasswordContextProvider"/>
<component-import key="appProps" interface="com.atlassian.sal.api.ApplicationProperties"/>
<resource name="view" type="velocity" location="/vm/password-input.vm"/>
<label key="issue-page-input.title"/>
</web-panel>
<!-- Active Objects module -->
<ao key="password-ao">
<description>The configuration of the Active Objects service</description>
<entity>tig.jira.extension.tigPasswordExtension.ao.PasswordModel</entity>
</ao>
<rest name="Password Resource" i18n-name-key="password-resource.name" key="password-resource" path="/passwordresource" version="1.0">
<description key="password-resource.description">The Password Resource Plugin</description>
</rest>
</atlassian-plugin>
tigPasswordExtension.js
AJS.toInit(function($) {
AJS.$('#searchButton2').click(function (){
var test = "lol";
AJS.$.ajax({
url:"jira/rest/passwordresource/1.0/password",
type: "post",
dataType: 'json',
async: false,
data: ({issueKey : document.getElementById("issueKeyInput").value, content: document.getElementById("passwordInput").value}),
success: function(data)
{
test = data;
console.log(test);
}
})
});
});
PasswordDto
package tig.jira.extension.tigPasswordExtension.dto;
public class PasswordDto {
private String issueKey;
private String content;
public PasswordDto(){}
public String getIssueKey(){return this.issueKey;}
public void setIssueKey(String issueKey){this.issueKey = issueKey;}
public String getContent(){return this.content;}
public void setContent(String content){this.content = content;}
}
PasswordModel
package tig.jira.extension.tigPasswordExtension.ao;
import net.java.ao.Entity;
import net.java.ao.Preload;
#Preload
public interface PasswordModel extends Entity {
String getIssue();
void setIssue(String issue);
String getContent();
void setContent(String content);
}
PasswordDao
package tig.jira.extension.tigPasswordExtension.ao;
import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
import com.google.common.collect.ImmutableMap.Builder;
import net.java.ao.Query;
import javax.inject.Inject;
import javax.inject.Named;
import java.util.Map;
#Named
public class PasswordDao {
private final ActiveObjects activeObjects;
#Inject
public PasswordDao(#ComponentImport ActiveObjects activeObjects){this.activeObjects = activeObjects;}
public PasswordModel getByIssueKey(String issueKey){
Query query = this.buildIssueQuery(issueKey);
PasswordModel[] passwordModels = this.activeObjects.find(PasswordModel.class, query);
return passwordModels.length > 0 ? passwordModels[0] : null;
}
public void update(String issueKey, String content){
PasswordModel passwordModel = this.getByIssueKey(issueKey);
if (passwordModel == null){
Map<String, Object> params = (new Builder()).put("ISSUE_KEY", issueKey).put("CONTENT", content).build();
this.activeObjects.create(PasswordModel.class, params);
} else {
passwordModel.setContent(content);
passwordModel.save();
}
}
private Query buildIssueQuery(String issueKey){
return Query.select().where("ISSUE_KEY = ?", issueKey);
}
}
PasswordResource
package tig.jira.extension.tigPasswordExtension.rest;
import tig.jira.extension.tigPasswordExtension.service.PasswordService;
import javax.ws.rs.*;
#Path("/password")
public class PasswordResource{
private final PasswordService passwordService;
public PasswordResource(PasswordService passwordService){this.passwordService = passwordService;}
#POST
public void update(#QueryParam("issue") String issueKey, #QueryParam("content") String content) {
this.passwordService.update(issueKey, content);
}
}
PasswordService
package tig.jira.extension.tigPasswordExtension.service;
import tig.jira.extension.tigPasswordExtension.ao.PasswordDao;
import tig.jira.extension.tigPasswordExtension.ao.PasswordModel;
import tig.jira.extension.tigPasswordExtension.dto.PasswordDto;
import javax.inject.Inject;
import javax.inject.Named;
#Named
public class PasswordService {
private final PasswordDao passwordDao;
#Inject
public PasswordService(PasswordDao passwordDao){
this.passwordDao = passwordDao;
}
public void update(String issueKey, String content){
this.passwordDao.update(issueKey, content);
}
public PasswordDto getByIssueKey(String issueKey){
PasswordModel passwordModel = this.passwordDao.getByIssueKey(issueKey);
if (passwordModel == null){
return null;
} else {
PasswordDto dto = new PasswordDto();
dto.setIssueKey(issueKey);
dto.setContent(passwordModel.getContent());
return dto;
}
}
}
PasswordContextProvider
package tig.jira.extension.tigPasswordExtension;
import com.atlassian.jira.issue.Issue;
import com.atlassian.jira.plugin.webfragment.contextproviders.AbstractJiraContextProvider;
import com.atlassian.jira.plugin.webfragment.model.JiraHelper;
import com.atlassian.jira.user.ApplicationUser;
import tig.jira.extension.tigPasswordExtension.dto.PasswordDto;
import tig.jira.extension.tigPasswordExtension.service.PasswordService;
import java.util.HashMap;
import java.util.Map;
public class PasswordContextProvider extends AbstractJiraContextProvider {
Map contextMap = new HashMap();
private String issueKey;
private final PasswordService passwordService;
public PasswordContextProvider(PasswordService passwordRepository){
this.passwordService = passwordRepository;
}
public Map getContextMap(ApplicationUser user, JiraHelper jiraHelper) {
Issue currentIssue = (Issue) jiraHelper.getContextParams().get("issue");
issueKey = currentIssue.getKey();
//passwordService.update(issueKey, "klote");
PasswordDto passwordDto = this.passwordService.getByIssueKey(issueKey);
if (passwordDto != null){
contextMap.put("AO", "1");
contextMap.put("content", "Staat nog niks in gek");
if (passwordDto.getContent() != null) {
contextMap.put("content", passwordDto.getContent());
}
}
contextMap.put("issueKey", issueKey);
return contextMap;
}
}
The only problem I could find after a quick look is in the class PasswordDao. If I replace the variable query by it's value then it will look like below.
activeObjects.find(PasswordModel.class, Query.select().where("ISSUE_KEY = ?", issueKey));
Whereas your entity class PasswordModel.java does not have any such method String xxxIssueKey(). Refer Offical document for column name of table created using ActiveObjects.
Best practice documentation can be a good start for you when working with Active Objects.
Update the method as given below (change ISSUE_KEY to ISSUE).
private Query buildIssueQuery(String issueKey){
return Query.select().where(" ISSUE = ? ", issueKey);
}
I hope this helps you.

NPE using QBOVendorService

I am trying to query vendors using the QBOVendorService but having no luck.
I am creating the service as follows:
QBOVendorService vService = QBServiceFactory.getService(context, QBOVendorService.class);
where the context is a valid PlatformSessionContext. I know the platform session context is good since I can get information about the user with it. When I try
vService.addVendor(context, vendor);
I end up with a NPE like my vService is null. Shouldn't I get an error initializing the QBOVendorService if it fails? Is there a good place to find more examples for using this since the intuit developer forums have been shut down?
I'm sharing a sample code snippet. Replace your OAuth tokens and relamId. It should work fine.
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import com.intuit.ds.qb.QBIdType;
import com.intuit.ds.qb.QBVendor;
import com.intuit.ds.qb.QBVendorQuery;
import com.intuit.ds.qb.QBVendorService;
import com.intuit.ds.qb.QBInvalidContextException;
import com.intuit.ds.qb.QBObjectFactory;
import com.intuit.ds.qb.QBServiceFactory;
import com.intuit.ds.qb.impl.QBRecordCountImpl;
import com.intuit.ds.qb.qbd.QBDRecordCountService;
import com.intuit.ds.qb.qbd.QBDServiceFactory;
import com.intuit.platform.client.PlatformSessionContext;
import com.intuit.platform.client.PlatformServiceType;
import com.intuit.platform.client.security.OAuthCredentials;
import com.intuit.ds.qb.QBSyncStatusRequest;
import com.intuit.ds.qb.QBSyncStatusRequestService;
import com.intuit.ds.qb.QBSyncStatusResponse;
import com.intuit.sb.cdm.NgIdSet;
import com.intuit.sb.cdm.ObjectName;
import org.slf4j.Logger;
// QBD API Docs - https://developer.intuit.com/docs/0025_quickbooksapi/0050_data_services/v2/0500_quickbooks_windows/0600_object_reference/vendor
// QBO API Docs - https://developer.intuit.com/docs/0025_quickbooksapi/0050_data_services/v2/0400_quickbooks_online/vendor
// JavaDocs - http://developer-static.intuit.com/SDKDocs/QBV2Doc/ipp-java-devkit-2.0.10-SNAPSHOT-javadoc/
public class CodegenStubVendorall {
final PlatformSessionContext context;
public CodegenStubVendorall(PlatformSessionContext context) {
this.context = context;
}
public void testAdd() {
final List<QBVendor> entityList = new ArrayList<QBVendor>();
try {
QBVendorService service = QBServiceFactory.getService(context, QBVendorService.class);
//Your Code
//Use Vendor POJO for creating Vendor
}
} catch (QBInvalidContextException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
PlatformSessionContext context = getPlatformContext();
CodegenStubVendorall testObj = new CodegenStubVendorall(context);
testObj.testAdd();
}
public static PlatformSessionContext getPlatformContext() {
String accesstoken = "rplce_your_application_token";
String accessstokensecret = "rplce_your_application_token";
String appToken = "rplce_your_application_token";
String oauth_consumer_key = "rplce_your_application_token";
String oauth_consumer_secret = "rplce_your_application_token";
String realmID = "123456";
String dataSource = "QBO";
PlatformServiceType serviceType;
if (dataSource.equalsIgnoreCase("QBO")) {
serviceType = PlatformServiceType.QBO;
} else {
serviceType = PlatformServiceType.QBD;
}
final OAuthCredentials oauthcredentials = new OAuthCredentials(
oauth_consumer_key, oauth_consumer_secret, accesstoken,
accessstokensecret);
final PlatformSessionContext context = new PlatformSessionContext(
oauthcredentials, appToken, serviceType, realmID);
return context;
}
}
You can try to use ApiExplorer tool to verify your OAuth tokens and to check the create Vendor API endpoint.
Link - https://developer.intuit.com/apiexplorer?apiname=V2QBO
Please let me know how it goes.
Thanks

Parsing nested JSON nodes to POJOs using Google Http Java Client

For example I have a response with the following JSON:
{
response: {
id: 20,
name: Stas
}
}
And I want to parse it to the following object:
class User {
private int id;
private String name;
}
How to skip the response node?
I use Google Http Java Client and it will be good if someone will answer how to do it there.
How will this lines have changed?
request.setParser(new JacksonFactory().createJsonObjectParser());
return request.execute().parseAs(getResultType());
You can now implement this in one step:
new JsonObjectParser.Builder(jsonFactory)
.setWrapperKeys(Arrays.asList("response"))
.build()
http://javadoc.google-http-java-client.googlecode.com/hg/1.15.0-rc/index.html
I do not know the Google Http Java Client, but if you can access the Jackson ObjectMapper you could do the following:
1.) Enable root unwrapping:
objectMapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
2.) Add annotation to User.class:
#JsonRootName("response")
class User {
…
}
I hope you can use this approach.
Edit: I dug through the google-http-java-client API and have come to the conclusion that you cannot access the ObjectMapper directly. In order to use the full power of Jackson you would have to write your own implementation of JsonObjectParser to wrap a 'real' Jackson parser. Sorry about that, maybe someone else could come up with a better solution.
I didn't find a native way (for this library) to solve my task. As a result I solved this problem by extending the functionality of JsonObjectParser. It entails expanding of the JacksonFactory, but it's a final class, so I used aggregation.
I wrote the following classes:
JacksonFilteringFactory
import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.json.jackson2.JacksonFactory;
public class JacksonFilteringFactory {
private final JacksonFactory factory = new JacksonFactory();
public JsonObjectParser createJsonObjectParser(String filteringNode) {
return new FilteringJsonObjectParser(factory, filteringNode);
}
}
FilteringJsonObjectParser
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.JsonObjectParser;
import com.vkredmessenger.AppController;
import com.vkredmessenger.util.StringUtils;
public class FilteringJsonObjectParser extends JsonObjectParser {
private String mFilteringNode;
public FilteringJsonObjectParser(JsonFactory jsonFactory,
String filteringNode) {
super(jsonFactory);
mFilteringNode = filteringNode;
}
#Override
public Object parseAndClose(InputStream in,
Charset charset, Type dataType)
throws IOException {
String originalResponse =
StringUtils.convertStreamToString(in, charset);
String response = null;
try {
JSONTokener tokener = new JSONTokener(originalResponse);
JSONObject originalResponseObject =
(JSONObject) tokener.nextValue();
JSONObject responseObject =
originalResponseObject.getJSONObject(mFilteringNode);
response = responseObject.toString();
} catch (JSONException e) {
e.printStackTrace();
}
InputStream filteredIn =
new ByteArrayInputStream(response.getBytes(charset));
return super.parseAndClose(filteredIn, charset, dataType);
}
}
So, for example from my question, the result parsing code will be the following:
request.setParser(new JacksonFilteringFactory().createJsonObjectParser("response"));
return request.execute().parseAs(getResultType());

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