Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Solace Read connector #31476

Merged
merged 66 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
f404eec
wip solace connector
bzablocki Apr 16, 2024
00274a2
wip solace connector
bzablocki Apr 16, 2024
399ffcc
some checker errors resolved
bzablocki Apr 17, 2024
62dfa4a
all checker errors resolved
bzablocki Apr 17, 2024
ea202fb
improving unit tests
bzablocki Apr 18, 2024
260f645
respond to pr commments
bzablocki Apr 19, 2024
9057860
Documentation
bzablocki Apr 19, 2024
a74b6a6
Small refactor - move data classes out of the client
bzablocki Apr 22, 2024
552cab4
refactor
bzablocki Apr 23, 2024
5d847b3
Add github action for integration test of Solace
bzablocki Apr 23, 2024
23292a1
testing github workflow
bzablocki Apr 24, 2024
a69e10a
bump testcontainers to 1.19.7 - soalce semp was updated with an admin…
bzablocki Apr 24, 2024
f4b0d6c
Use FlowHandle to acknowledge messages to make SolaceCheckpointMark's…
bzablocki Apr 29, 2024
a1ca8b3
Handle StaleSessionException error
bzablocki May 8, 2024
8007a33
Add @Internal annotation to mark the SolaceIO API beta and subject to…
bzablocki May 8, 2024
cff3105
Improve documentation
bzablocki May 13, 2024
f3eaabe
Back to ack based on bytesxmlmessages. Deduplicate default to false.
bzablocki May 29, 2024
34c4170
Merge remote-tracking branch 'origin/master' into solace-connector
bzablocki May 29, 2024
7b24fdb
update changes.md with Solace read connector
bzablocki May 29, 2024
be74c86
remove ack by id code
bzablocki Jun 3, 2024
a85c68c
remove todo comment
bzablocki Jun 3, 2024
533f122
Add licenses to package-info.java files
bzablocki Jun 3, 2024
2f41380
Restructure documentation
bzablocki Jun 3, 2024
9a317a8
update aws test after upgrading testcontainers version.
bzablocki Jun 5, 2024
38b6f0c
Merge branch 'master' of github.com:bzablocki/beam into solace-connector
bzablocki Jun 5, 2024
cf5d24d
Merge branch 'solace-connector' of github.com:bzablocki/beam into sol…
bzablocki Jun 5, 2024
aa29100
Disable publishing docs until the first pass on the master branch
bzablocki Jun 6, 2024
94101a8
Merge branch 'master' of github.com:apache/beam into solace-connector
bzablocki Jun 6, 2024
c625069
Remove files from this branch to split PR into smaller chunks
bzablocki Jun 6, 2024
1521f40
refactor tests for readability
bzablocki Jun 6, 2024
b815286
revert upgrade of testcontainers - not needed in this PR chunk
bzablocki Jun 6, 2024
08b777c
revert upgrade of testcontainers - not needed in this PR chunk
bzablocki Jun 6, 2024
0f63749
spotless
bzablocki Jun 6, 2024
e90e69a
remove IT tests from this pr
bzablocki Jun 6, 2024
f6833d2
Tech Writer review
bzablocki Jun 7, 2024
df1eb6c
Add a field to Solace.Record mapped from BytesXMLMessage.getAttachmen…
bzablocki Jun 7, 2024
5015b32
Add and fix some documentation
bzablocki Jun 10, 2024
2e1c10e
Remove CheckpointMark's reference to the UnboundedSolaceReader - unne…
bzablocki Jun 10, 2024
4d02b99
Revert "Remove CheckpointMark's reference to the UnboundedSolaceReade…
bzablocki Jun 11, 2024
bee8faf
Solace project init - github workflow file, gradle module
bzablocki Jun 11, 2024
a5b29ed
Merge branch 'solace-connector-0-project-init' into solace-connector
bzablocki Jun 11, 2024
09368d6
Merge branch 'master' of github.com:apache/beam into solace-connector
bzablocki Jun 13, 2024
f2d284a
Splitting the #31476 - Leaving only PTransform AutoValue configurations
bzablocki Jun 13, 2024
9af3185
remove unnecessary dependencies
bzablocki Jun 13, 2024
60d854d
remove info from CHANGES.md
bzablocki Jun 13, 2024
3847876
Add watermark-related code
bzablocki Jun 13, 2024
a00b997
Merge branch 'master' of github.com:apache/beam into solace-connector…
bzablocki Jun 14, 2024
19b02be
Remove excessive @Nullable annotations on Solace.Record class
bzablocki Jun 13, 2024
bcc0a51
Merge remote-tracking branch 'refs/remotes/upstream/master' into sola…
bzablocki Jun 14, 2024
0d59032
Remove entry from CHANGES.md
bzablocki Jun 14, 2024
2787260
Make Record builder package-private
bzablocki Jun 14, 2024
b46a8f8
Refactor SolaceIO - the constructor of Read takes a configuration bui…
bzablocki Jun 17, 2024
1c09b82
Change payload and attachments type to immutable ByteString
bzablocki Jun 17, 2024
81b7f84
Downgrade Record builders access modifiers to package private
bzablocki Jun 17, 2024
d1ada91
Add documentation
bzablocki Jun 17, 2024
15b394c
Add documentation to classes and methods in Solace.java
bzablocki Jun 17, 2024
2e536f9
typo
bzablocki Jun 17, 2024
daf2cfe
Add SolaceCheckpointMark.java
bzablocki Jun 17, 2024
93ffdd1
Merge branch 'refs/heads/solace-connector-0-watermark' into solace-co…
bzablocki Jun 17, 2024
9888b09
Merge branch 'refs/heads/solace-connector-0-checkpoint-mark' into sol…
bzablocki Jun 17, 2024
bbade93
Make SolaceCheckpointMark visible for testing
bzablocki Jun 17, 2024
4689fed
Merge remote-tracking branch 'refs/remotes/upstream/master' into sola…
bzablocki Jun 18, 2024
58ddecc
Merge remote-tracking branch 'upstream/master' into solace-connector
bzablocki Jun 18, 2024
a39f950
Merge remote-tracking branch 'upstream/master' into solace-connector
bzablocki Jun 18, 2024
da26ab5
Merge remote-tracking branch 'upstream/master' into solace-connector
bzablocki Jun 21, 2024
63425d7
Remove SolaceRecordCoder and take advantage of @DefaultSchema
bzablocki Jun 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions sdks/java/io/solace/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,16 @@ dependencies {
implementation library.java.slf4j_api
implementation library.java.joda_time
implementation library.java.solace
implementation library.java.vendored_guava_32_1_2_jre
implementation project(":sdks:java:extensions:avro")
implementation library.java.vendored_grpc_1_60_1
implementation library.java.avro
permitUnusedDeclared library.java.avro
implementation library.java.vendored_grpc_1_60_1

testImplementation library.java.junit
testImplementation project(path: ":sdks:java:io:common", configuration: "testRuntimeMigration")
testImplementation project(path: ":sdks:java:testing:test-utils", configuration: "testRuntimeMigration")
testRuntimeOnly library.java.slf4j_jdk14
testRuntimeOnly project(path: ":runners:direct-java", configuration: "shadow")
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,36 @@
*/
package org.apache.beam.sdk.io.solace.broker;

import com.solacesystems.jcsmp.Queue;
import java.io.Serializable;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* This abstract class serves as a blueprint for creating `SessionService` objects. It introduces a
* queue property and mandates the implementation of a create() method in concrete subclasses.
*/
public abstract class SessionServiceFactory implements Serializable {

/**
* A reference to a Queue object. This is set when the pipline is constructed (in the {@link
* org.apache.beam.sdk.io.solace.SolaceIO.Read#expand(org.apache.beam.sdk.values.PBegin)} method).
* This could be used to associate the created SessionService with a specific queue for message
* handling.
*/
@Nullable Queue queue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm watching whether Nullable is needed but not PR blocking at this point.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this has to be marked as @nullable. The SessionServiceFactory will be initialized when the pipeline is constructed, as one of the arguments to the SolaceIO. But the queue can only be set later, in the expand method.
Consider the scenario when a user creates a pipeline and specifies to read from a Topic - in this case we need to create a Queue (which is like a Subscription in PubSub) that will read from this Topic

Queue initializedQueue =
          queueFromOptions != null
              ? queueFromOptions
              : initializeQueueForTopic(jobName, sempClientFactory);

only then we can store the queue reference in the SessionServiceFactory

sessionServiceFactory.setQueue(initializedQueue);

I was trying to avoid it when designing it, but I couldn't find a better way around it.


/**
* This is the core method that subclasses must implement. It defines how to construct and return
* a SessionService object.
*/
public abstract SessionService create();

/**
* This method is called in the {@link
* org.apache.beam.sdk.io.solace.SolaceIO.Read#expand(org.apache.beam.sdk.values.PBegin)} method
* to set the Queue reference.
*/
public void setQueue(Queue queue) {
this.queue = queue;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import com.solacesystems.jcsmp.BytesXMLMessage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import org.apache.beam.sdk.schemas.AutoValueSchema;
import org.apache.beam.sdk.schemas.annotations.DefaultSchema;
import org.apache.beam.vendor.grpc.v1p60p1.com.google.protobuf.ByteString;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.slf4j.Logger;
Expand Down Expand Up @@ -74,6 +76,7 @@ public enum DestinationType {

/** Represents a Solace message destination (either a Topic or a Queue). */
@AutoValue
@DefaultSchema(AutoValueSchema.class)
public abstract static class Destination {
/**
* Gets the name of the destination.
Expand Down Expand Up @@ -105,6 +108,7 @@ abstract static class Builder {

/** Represents a Solace message record with its associated metadata. */
@AutoValue
@DefaultSchema(AutoValueSchema.class)
public abstract static class Record {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the following questions about this class.

  1. Would you mind to provide the error output that the SchemaFieldNumber annotations for each field in the following getters resolves?

  2. Could you provide the error output when you remove the Nullable annotations for the getters?

  3. For anything public, could we see Code comments?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ad 1) The SchemaFieldNumber annotations are necessary here as we stumbled upon error (#30276) related to non-deterministic schema inference.
ad 2) I can actually remove some of the @Nullable annotations here! Thank you for pointing it out.
ad 3) Good point, I'll work towards this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ad 2, 3 - done. I left only the necessary @Nullable annotations and added documentation

/**
* Gets the unique identifier of the message, a string for an application-specific message
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.beam.sdk.io.solace;

import org.apache.beam.sdk.io.solace.broker.MessageReceiver;
import org.apache.beam.sdk.io.solace.broker.SessionService;

public class MockEmptySessionService implements SessionService {

String exceptionMessage = "This is an empty client, use a MockSessionService instead.";

@Override
public void close() {
throw new UnsupportedOperationException(exceptionMessage);
}

@Override
public boolean isClosed() {
throw new UnsupportedOperationException(exceptionMessage);
}

@Override
public MessageReceiver createReceiver() {
throw new UnsupportedOperationException(exceptionMessage);
}

@Override
public void connect() {
throw new UnsupportedOperationException(exceptionMessage);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.beam.sdk.io.solace;

import com.solacesystems.jcsmp.JCSMPFactory;
import com.solacesystems.jcsmp.Queue;
import java.io.IOException;
import org.apache.beam.sdk.io.solace.broker.SempClient;
import org.apache.beam.sdk.transforms.SerializableFunction;

public class MockSempClient implements SempClient {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I heard for this IO there a plan to use https://java.testcontainers.org/modules/solace/? I think this is one of several PRs. Feel free resolve if this is the case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the testcontainers will be used for an integration test. A preliminary PR here: #31543

But there are also unit tests, which use the MockClients here.


private final SerializableFunction<String, Boolean> isQueueNonExclusiveFn;
private final SerializableFunction<String, Long> getBacklogBytesFn;
private final SerializableFunction<String, Integer> createQueueForTopicFn;

private MockSempClient(
SerializableFunction<String, Boolean> isQueueNonExclusiveFn,
SerializableFunction<String, Long> getBacklogBytesFn,
SerializableFunction<String, Integer> createQueueForTopicFn) {
this.isQueueNonExclusiveFn = isQueueNonExclusiveFn;
this.getBacklogBytesFn = getBacklogBytesFn;
this.createQueueForTopicFn = createQueueForTopicFn;
}

public static Builder builder() {
return new Builder();
}

public static class Builder {
private SerializableFunction<String, Boolean> isQueueNonExclusiveFn = (queueName) -> true;
private SerializableFunction<String, Long> getBacklogBytesFn = (queueName) -> 0L;
private SerializableFunction<String, Integer> createQueueForTopicFn = (queueName) -> 0;

public Builder setIsQueueNonExclusiveFn(
SerializableFunction<String, Boolean> isQueueNonExclusiveFn) {
this.isQueueNonExclusiveFn = isQueueNonExclusiveFn;
return this;
}

public Builder setGetBacklogBytesFn(SerializableFunction<String, Long> getBacklogBytesFn) {
this.getBacklogBytesFn = getBacklogBytesFn;
return this;
}

public Builder setCreateQueueForTopicFn(
SerializableFunction<String, Integer> createQueueForTopicFn) {
this.createQueueForTopicFn = createQueueForTopicFn;
return this;
}

public MockSempClient build() {
return new MockSempClient(isQueueNonExclusiveFn, getBacklogBytesFn, createQueueForTopicFn);
}
}

@Override
public boolean isQueueNonExclusive(String queueName) throws IOException {
return isQueueNonExclusiveFn.apply(queueName);
}

@Override
public Queue createQueueForTopic(String queueName, String topicName) throws IOException {
createQueueForTopicFn.apply(queueName);
return JCSMPFactory.onlyInstance().createQueue(queueName);
}

@Override
public long getBacklogBytes(String queueName) throws IOException {
return getBacklogBytesFn.apply(queueName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.beam.sdk.io.solace;

import org.apache.beam.sdk.io.solace.broker.SempClient;
import org.apache.beam.sdk.io.solace.broker.SempClientFactory;

public class MockSempClientFactory implements SempClientFactory {
SempClient sempClient;

public MockSempClientFactory(SempClient sempClient) {
this.sempClient = sempClient;
}

public static SempClientFactory getDefaultMock() {
return new MockSempClientFactory(MockSempClient.builder().build());
}

@Override
public SempClient create() {
return sempClient;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.beam.sdk.io.solace;

import com.solacesystems.jcsmp.BytesXMLMessage;
import java.io.IOException;
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.beam.sdk.io.solace.broker.MessageReceiver;
import org.apache.beam.sdk.io.solace.broker.SessionService;
import org.apache.beam.sdk.transforms.SerializableFunction;

public class MockSessionService implements SessionService {

private final SerializableFunction<Integer, BytesXMLMessage> getRecordFn;
private MessageReceiver messageReceiver = null;
private final int minMessagesReceived;

public MockSessionService(
SerializableFunction<Integer, BytesXMLMessage> getRecordFn, int minMessagesReceived) {
this.getRecordFn = getRecordFn;
this.minMessagesReceived = minMessagesReceived;
}

@Override
public void close() {}

@Override
public boolean isClosed() {
return false;
}

@Override
public MessageReceiver createReceiver() {
if (messageReceiver == null) {
messageReceiver = new MockReceiver(getRecordFn, minMessagesReceived);
}
return messageReceiver;
}

@Override
public void connect() {}

public static class MockReceiver implements MessageReceiver, Serializable {
private final AtomicInteger counter = new AtomicInteger();
private final SerializableFunction<Integer, BytesXMLMessage> getRecordFn;
private final int minMessagesReceived;

public MockReceiver(
SerializableFunction<Integer, BytesXMLMessage> getRecordFn, int minMessagesReceived) {
this.getRecordFn = getRecordFn;
this.minMessagesReceived = minMessagesReceived;
}

@Override
public void start() {}

@Override
public boolean isClosed() {
return false;
}

@Override
public BytesXMLMessage receive() throws IOException {
return getRecordFn.apply(counter.getAndIncrement());
}

@Override
public boolean isEOF() {
return counter.get() >= minMessagesReceived;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.beam.sdk.io.solace;

import org.apache.beam.sdk.io.solace.broker.SessionService;
import org.apache.beam.sdk.io.solace.broker.SessionServiceFactory;

public class MockSessionServiceFactory extends SessionServiceFactory {
SessionService sessionService;

public MockSessionServiceFactory(SessionService clientService) {
this.sessionService = clientService;
}

public static SessionServiceFactory getDefaultMock() {
return new MockSessionServiceFactory(new MockEmptySessionService());
}

@Override
public SessionService create() {
return sessionService;
}
}
Loading
Loading