Java built-in HttpClient doesn't remove subscribers fast enough - java

I have the following code:
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.json.JsonMapper;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import picocli.CommandLine;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Stream;
#CommandLine.Command(name = "red-alert-listener",
mixinStandardHelpOptions = true,
versionProvider = Listener.class,
showDefaultValues = true,
description = "An App that can get \"red alert\"s from IDF's Home Front Command.")
public class Listener implements Runnable, CommandLine.IVersionProvider
{
private static final Logger LOGGER = LogManager.getLogger();
private static final ObjectMapper JSON_MAPPER = new JsonMapper()
.enable(SerializationFeature.INDENT_OUTPUT)
.disable(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS)
.findAndRegisterModules();
private static final Configuration DEFAULT_CONFIGURATION = new Configuration(
false,
false,
true,
false,
Duration.ofMillis(10000),
LanguageCode.HE,
Level.INFO,
Collections.emptySet()
);
private static final HttpClient HTTP_CLIENT = HttpClient.newHttpClient();
private Configuration configuration = DEFAULT_CONFIGURATION;
private List<String> districtsNotFound = Collections.emptyList();
public static void main(String... args)
{
System.exit(new CommandLine(Listener.class)
.setCaseInsensitiveEnumValuesAllowed(true)
.execute(args));
}
private static void sleep()
{
try
{
Thread.sleep(1000);
} catch (InterruptedException interruptedException)
{
interruptedException.printStackTrace(); // TODO think about
}
}
#Override
public String[] getVersion()
{
return new String[]{"Red Alert Listener v" + getClass().getPackage().getImplementationVersion()};
}
#Override
public void run()
{
System.err.println("Preparing Red Alert Listener v" + getClass().getPackage().getImplementationVersion() + "...");
try (Clip clip = AudioSystem.getClip(Stream.of(AudioSystem.getMixerInfo()).parallel().unordered()
.filter(mixerInfo -> "default [default]".equals(mixerInfo.getName()))
.findAny()
.orElse(null));
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(Objects.requireNonNull(getClass().getResourceAsStream("/alarmSound.wav"))));
InputStream in = System.in)
{
clip.open(audioInputStream);
final URI uri = URI.create("https://www.oref.org.il/WarningMessages/alert/alerts.json");
Set<String> prevData = Collections.emptySet();
Instant currAlertsLastModified = Instant.MIN;
final int minRedAlertEventContentLength = """
{"cat":"1","data":[],"desc":"","id":0,"title":""}""".getBytes(StandardCharsets.UTF_8).length;
System.err.println("Listening...");
while (true)
try
{
final HttpResponse<InputStream> httpResponse = HTTP_CLIENT.send(
HttpRequest.newBuilder(uri)
.header("Accept", "application/json")
.header("X-Requested-With", "XMLHttpRequest")
.header("Referer", "https://www.oref.org.il/12481-" + configuration.languageCode().name().toLowerCase() + "/Pakar.aspx")
.timeout(configuration.timeout())
.build(),
HttpResponse.BodyHandlers.ofInputStream()
);
try (InputStream httpResponseBody = httpResponse.body())
{
if (httpResponse.statusCode() != HttpURLConnection.HTTP_OK/* &&
httpURLConnection.getResponseCode() != HttpURLConnection.HTTP_NOT_MODIFIED*/)
{
LOGGER.error("Connection response status code: {}", httpResponse.statusCode());
sleep();
continue;
}
final Instant alertsLastModified;
final long contentLength = httpResponse.headers().firstValueAsLong("Content-Length").orElse(-1);
if (contentLength < minRedAlertEventContentLength)
prevData = Collections.emptySet();
else if ((alertsLastModified = httpResponse.headers().firstValue("Last-Modified")
.map(lastModifiedStr -> DateTimeFormatter.RFC_1123_DATE_TIME.parse(lastModifiedStr, Instant::from))
.filter(currAlertsLastModified::isBefore)
.orElse(null)) != null)
{
currAlertsLastModified = alertsLastModified;
final RedAlertEvent redAlertEvent = JSON_MAPPER.readValue(
httpResponseBody,
RedAlertEvent.class
);
LOGGER.debug("Original event data: {}", redAlertEvent);
}
} catch (JsonParseException e)
{
LOGGER.error("JSON parsing error: {}", e.toString());
}
} catch (IOException e)
{
LOGGER.debug("Got exception: {}", e.toString());
sleep();
}
} catch (Throwable e)
{
LOGGER.fatal("Closing connection and exiting...", e);
}
}
#SuppressWarnings("unused")
private enum LanguageCode
{
HE,
EN,
AR,
RU;
}
private record RedAlertEvent(
int cat,
List<String> data,
String desc,
long id,
String title
)
{
}
private record Configuration(
boolean isMakeSound,
boolean isAlertAll,
boolean isDisplayResponse,
boolean isShowTestAlerts,
Duration timeout,
LanguageCode languageCode,
Level logLevel,
Set<String> districtsOfInterest
)
{
}
}
My dependencies:
<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.19.0</version>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.4.4</version>
</dependency>
<dependency>
<groupId>info.picocli</groupId>
<artifactId>picocli</artifactId>
<version>4.6.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.4</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.13.4</version>
</dependency>
<dependency>
<groupId>io.github.ashr123</groupId>
<artifactId>time-measurement</artifactId>
<version>1.0.7</version>
</dependency>
</dependencies>
I'm using OpenJDK 19 but it happens also on OpenJDK 17.
This is the most minimal code I can show you that demonstrate the problem.
When I used IntelliJ's profiler tool, I saw and here is a live CPU and heap charts (over 3 days):
I think that my HttpClient doesn't close the subscriptions fast enough (i.e. it adds HttpBodySubscriberWrapper via jdk.internal.net.http.HttpClientImpl#registerSubscriber faster that it removes it via jdk.internal.net.http.HttpClientImpl#subscriberCompleted).
Why does it happen?
Why even though I've put the Closeable body in try-with-resources block doesn't remove the subscriber in time for the next loop?
How can I control the size of the subscriber field?
Can I demand in any way that there is only 1 "uncompleted" subscriber?
UPDATE
I've discovered that for HttpResponse.BodyHandlers#ofString it doesn't happen (subscriber list is always of size 1), so the question is why if I keep the body in try-with-resources it and close the InputStream, it doesn't removes the request's subscriber?
From HttpResponse.BodySubscribers#ofInputStream javadoc:
API Note:
To ensure that all resources associated with the corresponding exchange are properly released the caller must ensure to either read all bytes until EOF is reached, or call InputStream.close if it is unable or unwilling to do so. Calling close before exhausting the stream may cause the underlying HTTP connection to be closed and prevent it from being reused for subsequent operations.
So is it a bug? is it something else?

Related

Mono retry with backoff not getting called for few elements in stream

There are stream of integers that I am trying to process. Incase of an exception I want to retry with backoff and at the same time backpressure will also be applied on the stream. I see that for few elements retry is not getting called.
To reproduce execute the below code and you should see code will be waiting indefinitely because queue becomes full coz for few elements doFinally is not called. In this case we should see count as 2000 but it will be less.
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
import reactor.util.retry.Retry;
import java.time.Duration;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;
public class MonoRetryTest {
private static final Logger logger = LoggerFactory.getLogger(MonoRetryTest.class);
private static BlockingQueue blockingQueue = new LinkedBlockingDeque(30);
private static final List values = Collections.synchronizedList(new LinkedList<Integer>());
#Test(timeout = 30000)
public void testMonoRetryWithBackOff() {
Flux.range(0,2000)
.publishOn(Schedulers.parallel())
.flatMap(e -> monoUtil(e))
.blockLast();
int processedValues = values.size();
Assert.assertTrue("Signal not recieved for all elements", 2000 == processedValues);
}
private static Mono monoUtil(int val) {
return Mono.just(1)
.flatMap(e -> {
try {
blockingQueue.put(val);
} catch (InterruptedException interruptedException) {
interruptedException.printStackTrace();
}
return serviceCall(Mono.just(val));
})
.doFinally(e -> {
blockingQueue.remove(val);
values.add(val);
logger.info("Finally Called "+ val);
});
}
private static Mono serviceCall(Mono<Integer> responseMono) {
return responseMono
.map(response -> test(response))
.retryWhen(Retry.backoff(3, Duration.ofMillis(10)))
.onErrorReturn("Failed");
}
private static String test(Integer value) {
if(value%5 == 0) throw new RuntimeException("Runtime Exception");
return "Success";
}
}

How do #Poller-s work in Spring Integration?

I am building an implementation of Sprint Integration with two PollableChannels:
Regular channel
Error channel
Messages are polled from the regular channel and processed. If there is an error during processing (e.g., an external service is unavailable), the message is sent into the error channel. From the error channel it is re-queued onto the regular channel, and the cycle continues until the message is successfully processed.
The idea is to poll the error channel infrequently, to give the processor some time to (hopefully) recover.
I have simulated this workflow in the following test:
package com.stackoverflow.questions.sipoller;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Poller;
import org.springframework.integration.annotation.Router;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.dsl.MessageChannels;
import org.springframework.messaging.Message;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.MessageBuilder;
import static org.awaitility.Awaitility.await;
import static org.awaitility.Durations.FIVE_MINUTES;
import static org.awaitility.Durations.ONE_HUNDRED_MILLISECONDS;
#SpringBootTest
class SiPollerApplicationTests {
private final static Logger LOG = LoggerFactory.getLogger(SiPollerApplicationTests.class);
private final static String QUEUE_CHANNEL_REGULAR = "queueChannelRegular";
private final static String QUEUE_CHANNEL_ERROR = "queueChannelError";
private final static String POLLER_PERIOD_REGULAR = "500"; // 0.5 second
private final static String POLLER_PERIOD_ERROR = "3000"; // 3 seconds
private final static AtomicInteger NUMBER_OF_ATTEMPTS = new AtomicInteger();
private final static AtomicInteger NUMBER_OF_SUCCESSES = new AtomicInteger();
private final static List<Instant> ATTEMPT_INSTANTS = Collections.synchronizedList(new ArrayList<>());
#Autowired
#Qualifier(QUEUE_CHANNEL_REGULAR)
private PollableChannel channelRegular;
#Test
void testTimingOfMessageProcessing() {
channelRegular.send(MessageBuilder.withPayload("Test message").build());
await()
.atMost(FIVE_MINUTES)
.with()
.pollInterval(ONE_HUNDRED_MILLISECONDS)
.until(
() -> {
if (NUMBER_OF_SUCCESSES.intValue() == 1) {
reportGaps();
return true;
}
return false;
}
);
}
private void reportGaps() {
List<Long> gaps = IntStream
.range(1, ATTEMPT_INSTANTS.size())
.mapToObj(
i -> Duration
.between(
ATTEMPT_INSTANTS.get(i - 1),
ATTEMPT_INSTANTS.get(i)
)
.toMillis()
)
.collect(Collectors.toList());
LOG.info("Gaps between attempts (in ms): {}", gaps);
}
#Configuration
#EnableIntegration
#Import(SiPollerApplicationTestEndpoint.class)
static class SiPollerApplicationTestConfig {
#Bean(name = QUEUE_CHANNEL_REGULAR)
public PollableChannel queueChannelRegular() {
return MessageChannels.queue(QUEUE_CHANNEL_REGULAR).get();
}
#Bean(name = QUEUE_CHANNEL_ERROR)
public PollableChannel queueChannelError() {
return MessageChannels.queue(QUEUE_CHANNEL_ERROR).get();
}
#Router(
inputChannel = QUEUE_CHANNEL_ERROR,
poller = #Poller(fixedRate = POLLER_PERIOD_ERROR)
)
public String retryProcessing() {
return QUEUE_CHANNEL_REGULAR;
}
}
#MessageEndpoint
static class SiPollerApplicationTestEndpoint {
#Autowired
#Qualifier(QUEUE_CHANNEL_ERROR)
private PollableChannel channelError;
#ServiceActivator(
inputChannel = QUEUE_CHANNEL_REGULAR,
poller = #Poller(fixedRate = POLLER_PERIOD_REGULAR)
)
public void handleMessage(Message<String> message) {
// Count and time attempts
int numberOfAttempts = NUMBER_OF_ATTEMPTS.getAndIncrement();
ATTEMPT_INSTANTS.add(Instant.now());
// First few times - refuse to process message and bounce it into
// error channel
if (numberOfAttempts < 5) {
channelError.send(message);
return;
}
// After that - process message
NUMBER_OF_SUCCESSES.getAndIncrement();
}
}
}
The pom.xml dependencies are:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.integration</groupId>
<artifactId>spring-integration-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<version>4.0.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Note the configuration for Pollers:
private final static String POLLER_PERIOD_REGULAR = "500"; // 0.5 second
private final static String POLLER_PERIOD_ERROR = "3000"; // 3 seconds
The regular channel is supposed to be polled once in half a second, and the error channel — once in three seconds.
The test simulates outages during message processing: the first five attempts to process the message are rejected. Also, the test records the Instant of every processing attempt. In the end, on my machine, the test outputs:
Gaps between attempts (in ms): [1, 0, 0, 0, 0]
In other words, the message is re-tried almost immediately after each failure.
It seems to me that I fundamentally misunderstand how Pollers work in Spring Integration. So my questions are:
Why is there such a dissonance between the poller configuration and the actual frequency of polling.
Does Spring Integration provide a way to implement the pattern I have described?
There are two settings that can affect this behavior.
QueueChannel pollers will drain the queue by default; setMaxMessagesPerPoll(1) to only receive one message each poll.
Also, by default, the QueueChannel default timeout is 1 second (1000ms).
So the first poll may be sooner than you think; set it to 0 to immediately exit if there are no messages present in the queue.

How to parse REST API STREAM

I am sending a get request to the server and server returns the following two responses. These responses are received as the event occurs on servers in streams (like id1,id2,id3,id4.....and so on) not in one shot.
Now, I need to take these response one by one and parse it and then save it the objects for further use.
How do i achieve this java 8 and spring MVC?
id: 1
data: {"event_type":"ABC","business_call_type":"XYZ","agent_number":"nnn","call_recording":null,"number":"0000","uuid":"a","call_direction":"Outbound","caller":"+100000000000","customer_number":"+100000000000","version":"1.0","k_number":"+917303454203","type":"AGENT_CALL","unique_id":"0","call_solution":"xx","FreeSWITCH_IPv4":"11111","Event_Date_Local":"2020-03-28 11:46:47"}
id: 2
data: {"event_type":"AGENT_ANSWER","business_call_type":"Outbound","agent_number":"+1111111111","call_recording":null,"number":"+22222222","uuid":"bbbbbbbbbbbbbb","call_direction":"Outbound","caller":"+100000000000","customer_number":"+100000000000","version":"1.0","k_number":"+1111111111","type":"AGENT_ANSWER","unique_id":"bbbbbbbbbb","call_solution":"xx","FreeSWITCH_IPv4":"0.0.0.0","Event_Date_Local":"2020-03-28 11:47:00"}
below is the code used foe above json parsing.
import java.util.HashMap;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Service;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.context.request.async.DeferredResult;
import com.psg.async_tasks.controller;
import com.psg.dao.CtiIntegrationdao;
// #Controller
#Service
public class ListningService {
private static final Logger logger = Logger.getLogger(ListningService.class.getName());
#Autowired
CtiIntegrationdao daoCtiInt;
//#RequestMapping({"list"})
#PostConstruct
public void ListningReponse() {
HashMap<String,String> results=daoCtiInt.getKnolarity_Config();
String endpoint;
endpoint=results.get("30");
endpoint=endpoint.replace("<<AUTH>>",results.get("26"));
logger.info(endpoint);
logger.info("============================================================================================#postconstruct=========");
AsyncRestTemplate asyncrestTemplate = new AsyncRestTemplate();
try {
final DeferredResult<String> result = new DeferredResult<>();
ListenableFuture<ResponseEntity<String>> futureEntity = asyncrestTemplate.getForEntity(endpoint, String.class);
logger.info("IN TRY");
logger.info(futureEntity.toString());
futureEntity.addCallback(new ListenableFutureCallback<ResponseEntity<String>>() {
#Override
public void onSuccess(ResponseEntity<String> result) {
String[] idno = result.getBody().split("\\R", 3);
System.out.println("==================="+idno[0]);
String responseBody =result.getBody().replaceAll("id: (\\d+)","").replace("data: ","");;
logger.info("-----responsebody-----"+responseBody);
logger.info("-----responsebody-----"+result.getBody());
// logger.info("-----responsebody-----"+result.getBody().getAgent_number());
// logger.info("-----responsebody-----"+result.getBody().getBusiness_call_type());
// logger.info("-----responsebody-----"+result.getBody().getCall_duration());
// logger.info("-----responsebody-----"+result.getBody().getCall_recording());
// logger.info("-----responsebody-----"+result.getBody().getCall_solution());
// logger.info("-----responsebody-----"+result.getBody().getCall_Type());
// logger.info("-----responsebody-----"+result.getBody().getDestination());
}
#Override
public void onFailure(Throwable ex) {
result.setErrorResult(ex.getMessage());
logger.info("------------Failure Block"+result.toString());
}
});
}catch(HttpClientErrorException ex) {
logger.info(ex.getMessage());
}catch(Exception ex) {
ex.printStackTrace();
}
}
}

how to access request body from request without content-type header in play

I see this is a tough question for Play. A lot of people asked that question, but still it is not clear how to get bytes from request body if content type is not set in Java.
There is a solution in Scala, but it is not working for my case.
I want to use Play to built a http mock server in a test in Java.
#BodyParser.Of(BodyParser.Raw.class) has no sense
package org.dan;
import org.junit.Test;
import play.mvc.BodyParser;
import play.mvc.Result;
import play.server.Server;
//import static play.mvc.Controller.request;
import static play.mvc.Results.ok;
import static play.routing.RoutingDsl.fromComponents;
import static play.server.Server.forRouter;
import static play.mvc.Http.Context.Implicit.request;
public class DemoPlayTest {
#Test
public void run() throws InterruptedException {
Server server = forRouter(
9001,
(components) ->
fromComponents(components)
.POST("/echo")
.routeTo(DemoPlayTest::action)
.build());
Thread.sleep(1111111);
}
#BodyParser.Of(BodyParser.Raw.class)
public static Result action() {
return ok("Gut: " + request().body().asRaw() + "\n");
}
}
Testing:
$ curl -v -X POST -d hello http://localhost:9001/echo
Gut: null
Dependencies:
<play.version>2.6.17</play.version>
<dependency>
<groupId>com.typesafe.play</groupId>
<artifactId>play-server_2.11</artifactId>
<version>${play.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.typesafe.play</groupId>
<artifactId>play-akka-http-server_2.11</artifactId>
<version>${play.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.typesafe.play</groupId>
<artifactId>play-java_2.11</artifactId>
<version>${play.version}</version>
<scope>test</scope>
</dependency>
package org.dan;
import org.junit.Test;
import play.mvc.Result;
import play.server.Server;
import static play.mvc.Controller.request;
import static play.mvc.Results.ok;
import static play.routing.RoutingDsl.fromComponents;
import static play.server.Server.forRouter;
public class DemoPlayTest {
#Test
public void run() throws InterruptedException {
Server server = forRouter(
9001,
(components) ->
fromComponents(components)
.POST("/echo")
.routeTo(DemoPlayTest::action)
.build());
Thread.sleep(1111111);
}
public static Result action() {
final String body = new String(request().body().asBytes().toArray());
return ok("Gut: " + body + "\n");
}
}

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

Categories

Resources