Kafka Streams Testing : java.util.NoSuchElementException: Uninitialized topic: "output_topic_name" - unit-testing

I've written a test class for kafka stream application as per https://kafka.apache.org/24/documentation/streams/developer-guide/testing.html
, the code for which is
import com.EventSerde;
import org.apache.kafka.common.serialization.Serde;
import org.apache.kafka.common.serialization.Serdes;
import org.apache.kafka.streams.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Properties;
public class KafkaStreamsConfigTest {
private TopologyTestDriver testDriver;
private TestInputTopic<String, Object> inputTopic;
private TestOutputTopic<String, Object> outputTopic;
private Serde<String> stringSerde = new Serdes.StringSerde();
private EventSerde eventSerde= new EventSerde();
private String key="test";
private Object value = "some value";
private Object expected_value = "real value";
String kafkaEventSourceTopic = "raw_events";
String kafkaEventSinkTopic = "processed_events";
String kafkaCacheSinkTopic = "cache_objects";
String applicationId = "my-app";
String test_dummy = "dummy:1234";
#Before
public void setup() {
Topology topology = new Topology();
topology.addSource(kafkaEventSourceTopic, kafkaEventSourceTopic);
topology.addProcessor(ProcessRouter.class.getSimpleName(), ProcessRouter::new, kafkaEventSourceTopic);
topology.addProcessor(WorkforceVisit.class.getSimpleName(), WorkforceVisit::new
, ProcessRouter.class.getSimpleName());
topology.addProcessor(DefaultProcessor.class.getSimpleName(), DefaultProcessor::new
, ProcessRouter.class.getSimpleName());
topology.addProcessor(CacheWorkforceShift.class.getSimpleName(), CacheWorkforceShift::new
, ProcessRouter.class.getSimpleName());
topology.addProcessor(DigitalcareShiftassisstantTracking.class.getSimpleName(), DigitalcareShiftassisstantTracking::new
, ProcessRouter.class.getSimpleName());
topology.addProcessor(WorkforceLocationUpdate.class.getSimpleName(), WorkforceLocationUpdate::new
, ProcessRouter.class.getSimpleName());
topology.addSink(kafkaEventSinkTopic, kafkaEventSinkTopic
, WorkforceVisit.class.getSimpleName(), DefaultProcessor.class.getSimpleName()
, CacheWorkforceShift.class.getSimpleName(), DigitalcareShiftassisstantTracking.class.getSimpleName()
, WorkforceLocationUpdate.class.getSimpleName());
topology.addSink(kafkaCacheSinkTopic, kafkaCacheSinkTopic
, WorkforceVisit.class.getSimpleName()
, CacheWorkforceShift.class.getSimpleName(), DigitalcareShiftassisstantTracking.class.getSimpleName()
, WorkforceLocationUpdate.class.getSimpleName());
Properties properties = new Properties();
properties.put(StreamsConfig.APPLICATION_ID_CONFIG, applicationId);
properties.put(StreamsConfig.BOOTSTRAP_SERVERS_CONFIG, test_dummy);
properties.put(StreamsConfig.DEFAULT_KEY_SERDE_CLASS_CONFIG, Serdes.String().getClass().getName());
properties.put(StreamsConfig.DEFAULT_VALUE_SERDE_CLASS_CONFIG, EventSerde.class.getName());
testDriver = new TopologyTestDriver(topology, properties);
//setup test topics
inputTopic = testDriver.createInputTopic(kafkaEventSourceTopic, stringSerde.serializer(), eventSerde.serializer());
outputTopic = testDriver.createOutputTopic(kafkaEventSinkTopic, stringSerde.deserializer(), eventSerde.deserializer());
}
#After
public void tearDown() {
testDriver.close();
}
#Test
public void outputEqualsTrue()
{
inputTopic.pipeInput(key, value);
Object b = outputTopic.readValue();
System.out.println(b.toString());
assertEquals(b,expected_value);
}
where I used EventSerde class to serialize and deserialize the value.
When I run this code it gives the error java.util.NoSuchElementException: Uninitialized topic: processed_events with the following stacktrace:
java.util.NoSuchElementException: Uninitialized topic: processed_events
at org.apache.kafka.streams.TopologyTestDriver.readRecord(TopologyTestDriver.java:715)
at org.apache.kafka.streams.TestOutputTopic.readRecord(TestOutputTopic.java:100)
at org.apache.kafka.streams.TestOutputTopic.readValue(TestOutputTopic.java:80)
at com.uhx.platform.eventprocessor.config.KafkaStreamsConfigTest.outputEqualsTrue(KafkaStreamsConfigTest.java:111)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
As you can see i have initialized both input and output topics.
I have also debugged the code and the error occurs when i read the value from output topic
outputTopic.readValue();
I don't understand what else i should do to initialize the outputTopic. Can anyone help me with this problem?
I am using apache kafka-streams-test-utils 2.4.0 and kafka-streams 2.4.0
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
<version>2.4.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients -->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>2.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams-test-utils</artifactId>
<version>2.4.0</version>
<scope>test</scope>
</dependency>

To avoid/overcome this exception, you need to check if your output topic is not empty before trying to read from it.
#Test
public void outputEqualsTrue()
{
inputTopic.pipeInput(key, value);
assert(outputTopic.isEmpty(), false);
Object b = outputTopic.readValue();
System.out.println(b.toString());
assertEquals(b,expected_value);
}

Related

Corda: Getting error - The Initiator of CollectSignaturesFlow must pass in exactly the sessions required to sign the transaction

I am following the Corda tutorial to build Corda app. Instead of building it in Kotlin, I built it in Java.
I followed the steps provided in the doc and below is my CarIssueInitiator class.
package com.template.flows;
import co.paralleluniverse.fibers.Suspendable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.template.contracts.CarContract;
import com.template.states.CarState;
import net.corda.core.contracts.Command;
import net.corda.core.contracts.UniqueIdentifier;
import net.corda.core.flows.*;
import net.corda.core.identity.Party;
import net.corda.core.transactions.SignedTransaction;
import net.corda.core.transactions.TransactionBuilder;
import net.corda.core.utilities.ProgressTracker;
import net.corda.core.utilities.ProgressTracker.Step;
import java.util.List;
import java.util.stream.Collectors;
// ******************
// * Initiator flow *
// ******************
#InitiatingFlow
#StartableByRPC
public class CarIssueInitiator extends FlowLogic<SignedTransaction> {
private final Party owningBank;
private final Party holdingDealer;
private final Party manufacturer;
private final String vin;
private final String licensePlateNumber;
private final String make;
private final String model;
private final String dealershipLocation;
private final Step GENERATING_TRANSACTION = new Step("Generating transaction based on new IOU.");
private final Step VERIFYING_TRANSACTION = new Step("Verifying contract constraints.");
private final Step SIGNING_TRANSACTION = new Step("Signing transaction with our private key.");
private final Step GATHERING_SIGS = new Step("Gathering the counterparty's signature.") {
#Override
public ProgressTracker childProgressTracker() {
return CollectSignaturesFlow.Companion.tracker();
}
};
private final Step FINALISING_TRANSACTION = new Step("Obtaining notary signature and recording transaction.") {
#Override
public ProgressTracker childProgressTracker() {
return FinalityFlow.Companion.tracker();
}
};
private final ProgressTracker progressTracker = new ProgressTracker(
GENERATING_TRANSACTION,
VERIFYING_TRANSACTION,
SIGNING_TRANSACTION,
GATHERING_SIGS,
FINALISING_TRANSACTION
);
public CarIssueInitiator(Party owningBank,
Party holdingDealer,
Party manufacturer,
String vin,
String licensePlateNumber,
String make,
String model,
String dealershipLocation){
this.owningBank = owningBank;
this.holdingDealer = holdingDealer;
this.manufacturer = manufacturer;
this.vin = vin;
this.licensePlateNumber = licensePlateNumber;
this.make = make;
this.model = model;
this.dealershipLocation = dealershipLocation;
}
#Override
public ProgressTracker getProgressTracker() {
return progressTracker;
}
#Suspendable
#Override
public SignedTransaction call() throws FlowException {
// Initiator flow logic goes here.
final Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
// Stage 1.
progressTracker.setCurrentStep(GENERATING_TRANSACTION);
// Generate an unsigned transaction.
Party me = getOurIdentity();
CarState carState = new CarState(this.owningBank,
this.holdingDealer,
this.manufacturer,
this.vin,
this.licensePlateNumber,
this.make,
this.model,
this.dealershipLocation,
new UniqueIdentifier());
final Command<CarContract.Commands.Issue> txCommand = new Command<CarContract.Commands.Issue>(new CarContract.Commands.Issue(),
ImmutableList.of(carState.getOwningBank().getOwningKey(), carState.getHoldingDealer().getOwningKey(), carState.getManufacturer().getOwningKey()));
final TransactionBuilder txBuilder = new TransactionBuilder(notary)
.addOutputState(carState, CarContract.ID)
.addCommand(txCommand);
// Stage 2.
progressTracker.setCurrentStep(VERIFYING_TRANSACTION);
// Verify that the transaction is valid.
txBuilder.verify(getServiceHub());
//Stage 3
progressTracker.setCurrentStep(SIGNING_TRANSACTION);
// Sign the transaction.
final SignedTransaction partSignedTx = getServiceHub().signInitialTransaction(txBuilder);
// Stage 4.
progressTracker.setCurrentStep(GATHERING_SIGS);
// Send the state to the counterparty, and receive it back with their signature.
List<FlowSession> sessions = carState.getParticipants().stream().map(a -> initiateFlow((Destination) a)).collect(Collectors.toList());
//FlowSession session = initiateFlow(me);
final SignedTransaction fullySignedTx = subFlow(
new CollectSignaturesFlow(partSignedTx, sessions, ImmutableList.of(me.getOwningKey()), CollectSignaturesFlow.Companion.tracker()));
// Stage 5.
progressTracker.setCurrentStep(FINALISING_TRANSACTION);
return subFlow(new FinalityFlow(fullySignedTx, sessions));
}
}
I deployed the CordApp and ran the flow. But I ended up with below error
java.lang.IllegalArgumentException: The Initiator of CollectSignaturesFlow must pass in exactly the sessions required to sign the transaction.
at net.corda.core.flows.CollectSignaturesFlow.call(CollectSignaturesFlow.kt:164) ~[corda-core-4.3.jar:?]
at net.corda.core.flows.CollectSignaturesFlow.call(CollectSignaturesFlow.kt:67) ~[corda-core-4.3.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.subFlow(FlowStateMachineImpl.kt:330) ~[corda-node-4.3.jar:?]
at net.corda.core.flows.FlowLogic.subFlow(FlowLogic.kt:326) ~[corda-core-4.3.jar:?]
at com.template.flows.CarIssueInitiator.call(CarIssueInitiator.java:129) ~[?:?]
at com.template.flows.CarIssueInitiator.call(CarIssueInitiator.java:23) ~[?:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:270) ~[corda-node-4.3.jar:?]
at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:46) ~[corda-node-4.3.jar:?]
at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092) ~[quasar-core-0.7.10-jdk8.jar:0.7.10]
at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788) ~[quasar-core-0.7.10-jdk8.jar:0.7.10]
at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100) ~[quasar-core-0.7.10-jdk8.jar:0.7.10]
at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91) ~[quasar-core-0.7.10-jdk8.jar:0.7.10]
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) ~[?:1.8.0_192]
at java.util.concurrent.FutureTask.run(Unknown Source) ~[?:1.8.0_192]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(Unknown Source) ~[?:1.8.0_192]
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(Unknown Source) ~[?:1.8.0_192]
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) ~[?:1.8.0_192]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) ~[?:1.8.0_192]
at net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor$1$thread$1.run(AffinityExecutor.kt:63) ~[corda-node-4.3.jar:?]
I am not getting what is wrong with the code. Request you to please help me out with this.
UPDATE: With below updated code, it works for me.
//Stage 3
progressTracker.setCurrentStep(SIGNING_TRANSACTION);
// Sign the transaction.
final PublicKey ourSigningKey = getServiceHub().getMyInfo().getLegalIdentities().get(0).getOwningKey();
final SignedTransaction partSignedTx = getServiceHub().signInitialTransaction(txBuilder, ourSigningKey);
// Stage 4.
progressTracker.setCurrentStep(GATHERING_SIGS);
// Send the state to the counterparty, and receive it back with their signature.
FlowSession HoldingDealerPartyFlow = initiateFlow(carState.getHoldingDealer());
FlowSession ManufacturerPartyFlow = initiateFlow(carState.getManufacturer());
final SignedTransaction fullySignedTx = subFlow(new CollectSignaturesFlow(
partSignedTx,
ImmutableSet.of(HoldingDealerPartyFlow, ManufacturerPartyFlow),
ImmutableList.of(ourSigningKey))
);
// Stage 5.
progressTracker.setCurrentStep(FINALISING_TRANSACTION);
return subFlow(new FinalityFlow(fullySignedTx, ImmutableSet.of(HoldingDealerPartyFlow, ManufacturerPartyFlow)));
The list of signers in carState.getParticipants()(see Code A) does not match with the list of signers in ImmutableList of CollectSignaturesFlow (see Code B).
Code A:
List<FlowSession> sessions = carState.getParticipants().stream().map(a -> initiateFlow((Destination) a)).collect(Collectors.toList());
Code B: final SignedTransaction fullySignedTx = subFlow(
new CollectSignaturesFlow(partSignedTx, sessions, ImmutableList.of(me.getOwningKey()), CollectSignaturesFlow.Companion.tracker()));
In the ImmutableList you have to add carState.getOwningBank().getOwningKey(), carState.getHoldingDealer().getOwningKey(), carState.getManufacturer().getOwningKey()
EDITED
Try the following code from "Stage 3" onwards
//Stage 3
progressTracker.setCurrentStep(SIGNING_TRANSACTION);
// Sign the transaction.
//final SignedTransaction partSignedTx = getServiceHub().signInitialTransaction(txBuilder);
val ourSigningKey = serviceHub.myInfo.legalIdentities.first().owningKey
val partSignedTx = serviceHub.signInitialTransaction(tx, ourSigningKey)
// Stage 4.
progressTracker.setCurrentStep(GATHERING_SIGS);
// Send the state to the counterparty, and receive it back with their signature.
//List<FlowSession> sessions = carState.getParticipants().stream().map(a -> initiateFlow((Destination) a)).collect(Collectors.toList());
//FlowSession session = initiateFlow(me);
//final SignedTransaction fullySignedTx = subFlow(
// new CollectSignaturesFlow(partSignedTx, sessions, ImmutableList.of(me.getOwningKey()), CollectSignaturesFlow.Companion.tracker()));
val HoldingDealerPartyFlow = initiateFlow(carState.getHoldingDealer())
val ManufacturerPartyFlow = initiateFlow(carState.getManufacturer())
val fullySignedTx = subFlow(CollectSignaturesFlow(
partSignedTx,
setOf(HoldingDealerPartyFlow ManufacturerPartyFlow),
listOf(ourSigningKey))
)
// Stage 5.
progressTracker.setCurrentStep(FINALISING_TRANSACTION);
//return subFlow(new FinalityFlow(fullySignedTx, sessions));
subFlow(FinalityFlow(fullySignedTx, setOf(HoldingDealerPartyFlow ManufacturerPartyFlow)))
return fullySignedTx.tx.outRef<State>(0)
You should only call initiateFlow() for all participants except for yourself, so your code could be like (the grammar might be wrong, but shows the point):
List<FlowSession> sessions = (carState.getParticipants() - me).stream().map(a -> initiateFlow((Destination) a)).collect(Collectors.toList());

How to use AWSRequestSigningApacheInterceptor with AWS SDK2

I am trying to use REST calls to Neptune SPARQL on existing Java code which already uses Apache HTTP clients. I'd like to not mix and match AWS SDK1 and SDK2 (which I use for the S3 portion of loading owl to Neptune).
I see these solutions:
AWSRequestSigningApacheInterceptor that works with SDK1, but can't find the equivalent in SDK2.
aws-request-signing-apache-interceptor on github for building an adaptor class so it can be used in SDK 2 with mix-and-match SDK 1 & 2
javaquery/Examples where Vicky Thakor has gone even more generic and just implemented the V4 signing for any Java REST implementation
But none of these is what I expected: an AWS or Apache implmentation of an Apache Interceptor for AWS SDK 2.
Is there such a thing? or is one of the above solutions the best available at the moment?
Here is some minimal code to make a few different authenticated REST requests to the ElasticSearch API (not Neptune SPARQL, but it's all REST).
pom.xml:
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>s3</artifactId>
<!-- version number is not needed due to the BOM below -->
</dependency>
<!-- below is needed for this issue: https://github.com/aws/aws-sdk-java-v2/issues/652 -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.11</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>apache-client</artifactId>
<!-- version number is not needed due to the BOM below -->
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>bom</artifactId>
<version>2.7.36</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
And here's the java:
import org.json.JSONObject;
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.auth.signer.Aws4Signer;
import software.amazon.awssdk.auth.signer.params.Aws4SignerParams;
import software.amazon.awssdk.http.*;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.http.apache.ApacheHttpClient;
import software.amazon.awssdk.utils.StringInputStream;
import java.io.*;
public class ElasticSearch implements Closeable {
private static final String HOST = "my-elasticsearch-3490jvoi2je3o.us-east-2.es.amazonaws.com";
private Aws4SignerParams params = Aws4SignerParams.builder()
.awsCredentials(DefaultCredentialsProvider.create().resolveCredentials())
.signingName("es") // "es" stands for elastic search. Change this to match your service!
.signingRegion(Region.US_EAST_2)
.build();
private Aws4Signer signer = Aws4Signer.create();
SdkHttpClient httpClient = ApacheHttpClient.builder().build();
/** #param path should not have a leading "/" */
private HttpExecuteResponse restRequest(SdkHttpMethod method, String path) throws IOException {
return restRequest(method, path, null);
}
private HttpExecuteResponse restRequest(SdkHttpMethod method, String path, JSONObject body)
throws IOException {
SdkHttpFullRequest.Builder b = SdkHttpFullRequest.builder()
.encodedPath(path)
.host(HOST)
.method(method)
.protocol("https");
if (body != null) {
b.putHeader("Content-Type", "application/json; charset=utf-8");
b.contentStreamProvider(() -> new StringInputStream(body.toString()));
}
SdkHttpFullRequest request = b.build();
// now sign it
SdkHttpFullRequest signedRequest = signer.sign(request, params);
HttpExecuteRequest.Builder rb = HttpExecuteRequest.builder().request(signedRequest);
// !!!: line below is necessary even though the contentStreamProvider is in the request.
// Otherwise the body will be missing from the request and auth signature will fail.
request.contentStreamProvider().ifPresent(c -> rb.contentStreamProvider(c));
return httpClient.prepareRequest(rb.build()).call();
}
public void search(String indexName, String searchString) throws IOException {
HttpExecuteResponse result = restRequest(SdkHttpMethod.GET, indexName+"/_search",
new JSONObject().put("query",
new JSONObject().put("match",
new JSONObject().put("txt",
new JSONObject().put("query", searchString)))));
System.out.println("Search results:");
System.out.println(new JSONObject(result.responseBody()));
}
/** #return success status */
public boolean createIndex(String indexName) throws IOException {
if (indexName.contains("/")) {
throw new RuntimeException("indexName cannot contain '/' character");
}
HttpExecuteResponse r = restRequest(SdkHttpMethod.PUT, indexName);
System.out.println("PUT /"+indexName + " response code: " + r.httpResponse().statusCode());
printInputStream(r.responseBody().get());
return r.httpResponse().isSuccessful();
}
private void printInputStream(InputStream is) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
String readLine;
while (((readLine = br.readLine()) != null)) System.out.println(readLine);
} catch (IOException e) {
e.printStackTrace();
}
}
public boolean postDoc(String indexName, String docId, JSONObject docBody) throws IOException {
HttpExecuteResponse response = restRequest(
SdkHttpMethod.PUT,
String.format("%s/_doc/%s", indexName, docId),
docBody
);
System.out.println("Index operation response:");
printInputStream(response.responseBody().get());
return response.httpResponse().isSuccessful();
}
#Override
public void close() throws IOException {
httpClient.close();
}
}
So, I settled on the second option with an important caveat: it does not handle AWS_SESSION_TOKEN. This is a simple fix. I've posted it along with the original answer at http://github.com/awslabs/aws-request-signing-apache-interceptor/
There's a new maintained fork of the archived awslabs aws-request-signing-apache-interceptor. It was upgraded to AWS SDK 2, and has a number of bug fixes, such as supporting retries. Version 2.1.1 was just released to Maven central.

JAX-WS: Assembling the enunciate app using CXF

I am customizing an appfuse 3.0.0 Web Service Only artifact. I have faced this stacktrace when trying to add a checked exception to my Web Service:
[ERROR] Failed to execute goal org.codehaus.enunciate:maven-enunciate-cxf-plugin:1.28:assemble (default) on project ibnInq: Problem assembling the enunciate app. local part cannot be "null" when creating a QName -> [Help 1]
org.apache.maven.lifecycle.LifecycleExecutionException: Failed to execute goal org.codehaus.enunciate:maven-enunciate-cxf-plugin:1.28:assemble (default) on project ibnInq: Problem assembling the enunciate app.
Problem assembling the enunciate app.
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:216)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.MojoExecutor.executeForkedExecutions(MojoExecutor.java:364)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:198)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:153)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:145)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:116)
at org.apache.maven.lifecycle.internal.LifecycleModuleBuilder.buildProject(LifecycleModuleBuilder.java:80)
at org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder.build(SingleThreadedBuilder.java:51)
at org.apache.maven.lifecycle.internal.LifecycleStarter.execute(LifecycleStarter.java:120)
at org.apache.maven.DefaultMaven.doExecute(DefaultMaven.java:355)
at org.apache.maven.DefaultMaven.execute(DefaultMaven.java:155)
at org.apache.maven.cli.MavenCli.execute(MavenCli.java:584)
at org.apache.maven.cli.MavenCli.doMain(MavenCli.java:216)
at org.apache.maven.cli.MavenCli.main(MavenCli.java:160)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.codehaus.plexus.classworlds.launcher.Launcher.launchEnhanced(Launcher.java:289)
at org.codehaus.plexus.classworlds.launcher.Launcher.launch(Launcher.java:229)
at org.codehaus.plexus.classworlds.launcher.Launcher.mainWithExitCode(Launcher.java:415)
at org.codehaus.plexus.classworlds.launcher.Launcher.main(Launcher.java:356)
Caused by: org.apache.maven.plugin.MojoExecutionException: Problem assembling the enunciate app.
at org.codehaus.enunciate.AssembleMojo.execute(AssembleMojo.java:75)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:132)
at org.apache.maven.lifecycle.internal.MojoExecutor.execute(MojoExecutor.java:208)
... 23 more
Caused by: java.lang.IllegalArgumentException: local part cannot be "null" when creating a QName
at javax.xml.namespace.QName.<init>(QName.java:244)
at javax.xml.namespace.QName.<init>(QName.java:188)
at org.codehaus.enunciate.contract.jaxws.WebFault.getParticleQName(WebFault.java:280)
at org.codehaus.enunciate.contract.jaxws.WebMethod.getReferencedNamespaces(WebMethod.java:232)
at org.codehaus.enunciate.contract.jaxws.EndpointInterface.getReferencedNamespaces(EndpointInterface.java:229)
at org.codehaus.enunciate.config.WsdlInfo.getImportedNamespaces(WsdlInfo.java:132)
at org.codehaus.enunciate.modules.xml.WsdlInfoModel.get(WsdlInfoModel.java:50)
at freemarker.core.Dot._getAsTemplateModel(Dot.java:76)
at freemarker.core.Expression.getAsTemplateModel(Expression.java:89)
at freemarker.core.IteratorBlock.accept(IteratorBlock.java:94)
at freemarker.core.Environment.visit(Environment.java:221)
at freemarker.core.MixedContent.accept(MixedContent.java:92)
at freemarker.core.Environment.visit(Environment.java:221)
at freemarker.core.Macro$Context.runMacro(Macro.java:172)
at freemarker.core.Environment.visit(Environment.java:614)
at freemarker.core.UnifiedCall.accept(UnifiedCall.java:106)
at freemarker.core.Environment.visit(Environment.java:221)
at freemarker.core.Environment.visit(Environment.java:310)
at freemarker.core.CompressedBlock.accept(CompressedBlock.java:73)
at freemarker.core.Environment.visit(Environment.java:221)
at freemarker.core.Environment.visit(Environment.java:310)
at freemarker.core.UnifiedCall.accept(UnifiedCall.java:130)
at freemarker.core.Environment.visit(Environment.java:221)
at freemarker.core.ConditionalBlock.accept(ConditionalBlock.java:79)
at freemarker.core.Environment.visit(Environment.java:221)
at freemarker.core.Environment.visit(Environment.java:310)
at freemarker.core.UnifiedCall.accept(UnifiedCall.java:130)
at freemarker.core.Environment.visit(Environment.java:221)
at freemarker.core.MixedContent.accept(MixedContent.java:92)
at freemarker.core.Environment.visit(Environment.java:221)
at freemarker.core.Environment.process(Environment.java:199)
at freemarker.template.Template.process(Template.java:259)
at org.codehaus.enunciate.modules.FreemarkerDeploymentModule.processTemplate(FreemarkerDeploymentModule.java:106)
at org.codehaus.enunciate.modules.FreemarkerDeploymentModule.processTemplate(FreemarkerDeploymentModule.java:85)
at org.codehaus.enunciate.modules.FreemarkerDeploymentModule.processTemplate(FreemarkerDeploymentModule.java:70)
at org.codehaus.enunciate.modules.xml.XMLDeploymentModule.doFreemarkerGenerate(XMLDeploymentModule.java:340)
at org.codehaus.enunciate.modules.FreemarkerDeploymentModule.doGenerate(FreemarkerDeploymentModule.java:51)
at org.codehaus.enunciate.modules.BasicDeploymentModule.step(BasicDeploymentModule.java:107)
at org.codehaus.enunciate.apt.EnunciateAnnotationProcessor.process(EnunciateAnnotationProcessor.java:128)
at com.sun.mirror.apt.AnnotationProcessors$CompositeAnnotationProcessor.process(AnnotationProcessors.java:84)
at com.sun.tools.apt.comp.Apt.main(Apt.java:480)
at com.sun.tools.apt.main.AptJavaCompiler.compile(AptJavaCompiler.java:270)
at com.sun.tools.apt.main.Main.compile(Main.java:1127)
at com.sun.tools.apt.main.Main.compile(Main.java:989)
at com.sun.tools.apt.Main.processing(Main.java:113)
at com.sun.tools.apt.Main.process(Main.java:103)
at com.sun.tools.apt.Main.process(Main.java:85)
at org.codehaus.enunciate.main.Enunciate.invokeApt(Enunciate.java:817)
at org.codehaus.enunciate.main.Enunciate.doGenerate(Enunciate.java:401)
at org.codehaus.enunciate.ConfigMojo$MavenSpecificEnunciate.doGenerate(ConfigMojo.java:670)
at org.codehaus.enunciate.main.Enunciate$Stepper.step(Enunciate.java:1799)
at org.codehaus.enunciate.main.Enunciate$Stepper.stepTo(Enunciate.java:1831)
at org.codehaus.enunciate.AssembleMojo.execute(AssembleMojo.java:71)
... 25 more
My enunciate version is 1.28, and I have enabled cxf module in enunciate.xml:
<cxf disabled="false"/>
I have used JAX-WS Spec for exception throwing.
Any help is appreciated.
Finally I have done following workaround to fix my problem: adding #XmlRootElement to my FaultBean.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "FaultBean",propOrder = {"errorDescription",errorCode"})
public class FaultBean implements Serializable {
#XmlElement(required = true, nillable = true)
protected String errorDescription;
#XmlElement(required = true, nillable = true)
protected String errorCode;
public FaultBean() {
}
public String getErrorDescription() {
return this.errorDescription;
}
public void setErrorDescription(String var1) {
this.errorDescription = var1;
}
public String getErrorCode() {
return this.errorCode;
}
public void setErrorCode(String var1) {
this.errorCode = var1;
}
}

Error in Writing a Custom OutputAttributeProcessor

I have a question and problem about writing a custom OutputAttributeProcessor.
I use WSO2 CEP 2.1.0 and siddhi 1.1.0.
I want to create a custom OutputAttributeProcessor, so I create two java classes, DiscomfortIndexAggregatorFactory implements OutputAttributeProcessorFactory and DiscomfortIndexAggregator implements OutputAttributeProcessor.
Package of two classes is org.wso2.siddhi.extention.aggregator.environment.
Two java programs are as follows.
DiscomfortIndexAggregatorFactory.java
package org.wso2.siddhi.extention.aggregator.environment;
import org.wso2.siddhi.core.query.projector.attribute.factory.OutputAttributeProcessorFactory;
import org.wso2.siddhi.core.query.projector.attribute.handler.OutputAttributeProcessor;
import org.wso2.siddhi.query.api.definition.Attribute.Type;
import org.wso2.siddhi.query.api.extension.annotation.SiddhiExtension;
#SiddhiExtension(namespace = "environment", function = "discomfortIndex")
public class DiscomfortIndexAggregatorFactory implements
OutputAttributeProcessorFactory {
#Override
public OutputAttributeProcessor createAggregator(Type type) {
return new DiscomfortIndexAggregator();
}
#Override
public ProcessorType getProcessorType() {
return OutputAttributeProcessorFactory.ProcessorType.AGGREGATOR;
}
}
DiscomfortIndexAggregator.java
package org.wso2.siddhi.extention.aggregator.environment;
import org.wso2.siddhi.core.query.projector.attribute.handler.OutputAttributeProcessor;
import org.wso2.siddhi.query.api.definition.Attribute.Type;
public class DiscomfortIndexAggregator implements OutputAttributeProcessor {
private static final long serialVersionUID = -5992266303998509661L;
#Override
public OutputAttributeProcessor createNewInstance() {
return new DiscomfortIndexAggregator();
}
#Override
public Type getType() {
return Type.DOUBLE;
}
#Override
public Object processInEventAttribute(Object obj) {
double discomfortIndex = -1D;
if (obj instanceof Object[]) {
Object[] objArray = (Object[]) obj;
double temperature = (double) objArray[0];
double humidity = (double) objArray[1];
discomfortIndex = 0.81 * temperature + 0.01 * humidity
* (0.99 * temperature - 14.3) + 46.3;
}
return discomfortIndex;
}
#Override
public Object processRemoveEventAttribute(Object obj) {
double discomfortIndex = -1D;
if (obj instanceof Object[]) {
Object[] objArray = (Object[]) obj;
double temperature = (double) objArray[0];
double humidity = (double) objArray[1];
discomfortIndex = 0.81 * temperature + 0.01 * humidity
* (0.99 * temperature - 14.3) + 46.3;
}
return discomfortIndex;
}
}
I created jar file consists of two java classes, added the jar file to the class path at /repository/components/lib, and added siddhi.extension file located at /repository/conf/siddhi.
Content of siddhi.extention is as follows.
org.wso2.siddhi.extention.aggregator.environment.DiscomfortIndexAggregatorFactory
I restarted after above configuration.
Error logs are not output after restart.
But I created following query of bucket,
from hiroshimaData
insert into disconfortIndexStream
environment:discomfortIndex(RTC_001_Temp, SHT_001_Humi) as discomfortIndex
and following error log is output.
ERROR {org.wso2.carbon.cep.core.BucketDeployer} - wrong configuration provided for adding HIROSHIMA.xml
org.wso2.carbon.cep.core.exception.CEPConfigurationException: Error in initializing Siddhi backend Runtime,null
at org.wso2.carbon.cep.core.internal.CEPBucket.init(CEPBucket.java:109)
at org.wso2.carbon.cep.core.internal.CEPService.deployBucket(CEPService.java:213)
at org.wso2.carbon.cep.core.internal.CEPService.deployBucket(CEPService.java:174)
at org.wso2.carbon.cep.core.BucketDeployer.deploy(BucketDeployer.java:95)
at org.apache.axis2.deployment.repository.util.DeploymentFileData.deploy(DeploymentFileData.java:136)
at org.apache.axis2.deployment.DeploymentEngine.doDeploy(DeploymentEngine.java:810)
at org.apache.axis2.deployment.repository.util.WSInfoList.update(WSInfoList.java:144)
at org.apache.axis2.deployment.RepositoryListener.update(RepositoryListener.java:377)
at org.apache.axis2.deployment.RepositoryListener.checkServices(RepositoryListener.java:254)
at org.apache.axis2.deployment.RepositoryListener.startListener(RepositoryListener.java:371)
at org.apache.axis2.deployment.scheduler.SchedulerTask.checkRepository(SchedulerTask.java:59)
at org.apache.axis2.deployment.scheduler.SchedulerTask.run(SchedulerTask.java:67)
at org.wso2.carbon.core.deployment.CarbonDeploymentSchedulerTask.runAxisDeployment(CarbonDeploymentSchedulerTask.java:67)
at org.wso2.carbon.core.deployment.CarbonDeploymentSchedulerTask.run(CarbonDeploymentSchedulerTask.java:112)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:304)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$301(ScheduledThreadPoolExecutor.java:178)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Caused by: java.lang.NullPointerException
at org.wso2.siddhi.core.query.projector.attribute.processor.AbstractAggregationAttributeProcessor.load(AbstractAggregationAttributeProcessor.java:88)
at org.wso2.siddhi.core.persistence.PersistenceService.restoreRevision(PersistenceService.java:79)
at org.wso2.siddhi.core.persistence.PersistenceService.restoreLastRevision(PersistenceService.java:104)
at org.wso2.siddhi.core.SiddhiManager.restoreLastRevision(SiddhiManager.java:276)
at org.wso2.carbon.cep.siddhi.backend.SiddhiBackEndRuntime.init(SiddhiBackEndRuntime.java:259)
at org.wso2.carbon.cep.core.internal.CEPBucket.init(CEPBucket.java:107)
Can you tell me how to do it?
Thank you in advance.

Modify response of web service with JAX-WS

How can I modify the namespace of the response like this:
old response:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<ns2:GetAmountResponse xmlns:ns2="http://ws.dsi.otn.com/dab">
<etat>0</etat>
<montant>500.0</montant>
</ns2:GetAmountResponse>
</soap:Body>
</soap:Envelope>
new response wanted :
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<GetAmountResponse xmlns="http://ws.dsi.otn.com/dab">
<etat>0</etat>
<montant>500.0</montant>
</GetAmountResponse>
</soap:Body>
</soap:Envelope>
I want to remove the ns2 namespce prefix.
In the first case, the GetAmountResponse is in namespace http://ws.dsi.otn.com/dab while etat and montant are in a default (empty) namespace.
In the new message you want, GetAmountResponse, etat and montant are all in namespace http://ws.dsi.otn.com/dab.
The namespaces can be controlled from the namespaces of your classes. Use the same namespace in all and you will have them in the same namespace, leave classes with defaults and they default to empty namespace.
For example, if you were to have something like this in your web service class:
#WebMethod
public
#WebResult(name = "getAmountResponse", targetNamespace = "http://ws.dsi.otn.com/dab")
AmountResponse getAmount(
#WebParam(name = "getAmountRequest", targetNamespace = "http://ws.dsi.otn.com/dab") AmountRequest request) {
AmountResponse response = new AmountResponse();
response.setEtat(0);
response.setMontant(500.0);
return response;
}
with a response class like this:
#XmlRootElement
public class AmountResponse {
private int etat;
private double montant;
// getter and setters omitted
}
you will end up with the first type of soap message.
But if you change the response class to look like this instead:
#XmlRootElement(namespace = "http://ws.dsi.otn.com/dab")
#XmlAccessorType(XmlAccessType.NONE)
public class AmountResponse {
#XmlElement(namespace = "http://ws.dsi.otn.com/dab")
private int etat;
#XmlElement(namespace = "http://ws.dsi.otn.com/dab")
private double montant;
// getters and setter omitted
}
you will bring all tags in the same namespace and you get something equivalent to the new type of message you want. I said equivalent because I don't think you will get exactly this:
<GetAmountResponse xmlns="http://ws.dsi.otn.com/dab">
<etat>0</etat>
<montant>500.0</montant>
</GetAmountResponse>
It's more likely to get something like this instead:
<ns2:getAmountResponse xmlns:ns2="http://ws.dsi.otn.com/dab">
<ns2:etat>0</ns2:etat>
<ns2:montant>500.0</ns2:montant>
</ns2:getAmountResponse>
It's the same "XML meaning" for both messages although they don't look the same.
If you absolutely want it to look like that, I think you will have to go "low level" and use something like a SOAP handler to intercept the response and modify it. But be aware that it won't be a trivial task to change the message before it goes on the wire.
logical handler are enough to transform to the message as expected :
package com.ouertani.slim;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.LogicalMessage;
import javax.xml.ws.handler.LogicalHandler;
import javax.xml.ws.handler.LogicalMessageContext;
import javax.xml.ws.handler.MessageContext;
/**
*
* #author ouertani
*/
public class MyLogicalHandler implements LogicalHandler<LogicalMessageContext> {
#Override
public boolean handleMessage(LogicalMessageContext messageContext) {
/// extract state and amount
int state = 0;
double amount = 200.0;
transform(messageContext, state, amount);
return false;
}
public boolean handleFault(LogicalMessageContext messageContext) {
return true;
}
public void close(MessageContext context) {
}
private void transform( LogicalMessageContext messageContext, int etat, double montant){
LogicalMessage msg = messageContext.getMessage();
String htom = "<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">"+
"<soap:Body>"+
"<GetAmountResponse xmlns=\"http://ws.dsi.otn.com/dab\">"+
"<etat>"+etat+"</etat>"+
"<montant>"+montant+"</montant>"+
"</GetAmountResponse>"+
"</soap:Body>"+
"</soap:Envelope>";
InputStream is = new ByteArrayInputStream(htom.getBytes());
Source ht = new StreamSource(is);
msg.setPayload(ht);
}
}
This is a very old question, still it is yet to be effectively answered. This week I faced a very similar problem. My application is invoking a Soap web-service provided by a legacy system whose XML is response syntactically wrong with some empty characters (line break, or tabs or white spaces) before XML declaration. In my scenario I could not change the legacy system to fix its response so changing the response before parsing was the only alternative I was left with.
Here is my solution:
I have added the following maven dependencies to my application:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>javax.xml.ws</groupId>
<artifactId>jaxws-api</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.sun.xml.ws</groupId>
<artifactId>jaxws-rt</artifactId>
<version>2.3.0</version>
</dependency>
Then I have registered a Java SPI custom implementation of “com.oracle.webservices.impl.internalspi.encoding.StreamDecoder”. This class is invoked immediately before the XML parse with the corresponding response InputStream, so at this point you can read the response InputStream or wrap/proxy it and make any change to jax-ws response before parsing. In my case I just remove some invisible characters before first visible character.
My StreamDecoder SPI implementation:
package sample.streamdecoder;
import com.oracle.webservices.impl.encoding.StreamDecoderImpl;
import com.oracle.webservices.impl.internalspi.encoding.StreamDecoder;
import com.sun.xml.ws.api.SOAPVersion;
import com.sun.xml.ws.api.message.AttachmentSet;
import com.sun.xml.ws.api.message.Message;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
public class MyStreamDecoder implements StreamDecoder {
//JAX-WS default implementation
private static final StreamDecoderImpl streamDecoder = new StreamDecoderImpl();
#Override
public Message decode(InputStream inputStream, String charset, AttachmentSet attachmentSet, SOAPVersion soapVersion) throws IOException {
//Wrapping inputStream
InputStream wrapped = wrapInputStreamStrippingBlankCharactersBeforeXML(inputStream, charset);
//Delegating further processing to default StreamDecoder
return streamDecoder.decode(wrapped, charset, attachmentSet, soapVersion);
}
private InputStream wrapInputStreamStrippingBlankCharactersBeforeXML(InputStream inputStream, String charset) throws IOException {
int WHITESPACE = (int) Charset.forName(charset).encode(" ").get();
int LINE_BREAK = (int) Charset.forName(charset).encode("\n").get();
int TAB = (int) Charset.forName(charset).encode("\t").get();
return new InputStream() {
private boolean xmlBegin = true;
#Override
public int read() throws IOException {
int read = inputStream.read();
if (!xmlBegin) {
return read;
} else {
while (WHITESPACE == read
|| LINE_BREAK == read
|| TAB == read) {
read = inputStream.read();
}
xmlBegin = false;
}
return read;
}
};
}
}
In order to register it, just create a file “META-INF/services/ com.oracle.webservices.impl.internalspi.encoding.StreamDecoder” named “” and write the fully qualified name of your SPI implementation on the first line like that:
Content of file META-INF/services/ com.oracle.webservices.impl.internalspi.encoding.StreamDecoder :
sample.streamdecoder.MyStreamDecoder
Now every response will be passed to you implementation before parse.