datai/datai-scenes/datai-scene-salesforce/docs/reference-code/salesforce-pubsub-realtime-sync/PubSubConnectionFactory.java
Kris be3640121c feat: 添加 Pub/Sub API 配置项并优化连接工厂
- 新增 salesforce.pubsub.consumer.key 配置项 (ID: 171)
- 新增 salesforce.pubsub.consumer.secret 配置项 (ID: 172)
- 优化 PubSubConnectionFactory.createStsCredentials 方法,从缓存读取配置值
- 注入 SalesforceConfigCacheManager 依赖
- 添加变更记录文档 0008-pubsub-config-optimization.md
- 更新文档索引
2026-01-12 17:02:32 +08:00

115 lines
4.1 KiB
Java
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.datai.integration.factory.impl;
import com.salesforce.eventbus.protobuf.PubSubGrpc;
import com.sforce.soap.partner.PartnerConnection;
import com.sforce.ws.ConnectionException;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.Metadata;
import io.grpc.stub.MetadataUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import java.net.URI;
import java.util.concurrent.TimeUnit;
/**
* Pub/Sub API 连接工厂
* 负责构建带有认证头的 gRPC Stub
*/
@Slf4j
@Component
public class PubSubConnectionFactory {
@Autowired
private SOAPConnectionFactory soapConnectionFactory; // 复用现有的 SOAP 登录逻辑
private ManagedChannel channel;
private PubSubGrpc.PubSubStub asyncStub;
private PubSubGrpc.PubSubBlockingStub blockingStub;
/**
* 获取或创建 gRPC Stub (异步)
*/
public synchronized PubSubGrpc.PubSubStub getAsyncStub() {
ensureConnected();
return asyncStub;
}
/**
* 获取或创建 gRPC Stub (同步,用于获取 Schema)
*/
public synchronized PubSubGrpc.PubSubBlockingStub getBlockingStub() {
ensureConnected();
return blockingStub;
}
private void ensureConnected() {
if (channel != null && !channel.isShutdown() && !channel.isTerminated()) {
return;
}
log.info("初始化 Salesforce Pub/Sub API gRPC 连接...");
createChannelAndStubs();
}
private void createChannelAndStubs() {
try {
// 1. 获取认证信息 (Session ID & Endpoint)
PartnerConnection connection = (PartnerConnection) soapConnectionFactory.getConnection();
String sessionId = connection.getConfig().getSessionId();
String serverUrl = connection.getConfig().getServiceEndpoint();
// 解析 Endpoint (e.g., https://instance.salesforce.com/...)
URI uri = new URI(serverUrl);
String host = uri.getHost();
int port = 443; // gRPC 默认端口
// 2. 构造 Metadata Headers
Metadata headers = new Metadata();
Metadata.Key<String> AUTH_KEY = Metadata.Key.of("accesstoken", Metadata.ASCII_STRING_MARSHALLER);
Metadata.Key<String> INSTANCE_KEY = Metadata.Key.of("instanceurl", Metadata.ASCII_STRING_MARSHALLER);
Metadata.Key<String> TENANT_KEY = Metadata.Key.of("tenantid", Metadata.ASCII_STRING_MARSHALLER);
headers.put(AUTH_KEY, sessionId);
headers.put(INSTANCE_KEY, serverUrl);
try {
// 尝试获取 Tenant ID如果 SOAP API 未提供,部分环境可忽略
headers.put(TENANT_KEY, connection.getUserInfo().getOrganizationId());
} catch (Exception e) {
log.warn("无法自动获取 TenantId将尝试不带 TenantId 连接");
}
// 3. 创建 Channel
this.channel = ManagedChannelBuilder.forAddress(host, port)
.useTransportSecurity() // 必须使用 TLS
.keepAliveTime(60, TimeUnit.SECONDS)
.build();
// 4. 创建 Stubs 并注入 Header
this.asyncStub = MetadataUtils.attachHeaders(PubSubGrpc.newStub(channel), headers);
this.blockingStub = MetadataUtils.attachHeaders(PubSubGrpc.newBlockingStub(channel), headers);
log.info("Salesforce Pub/Sub API 连接初始化成功。Host: {}", host);
} catch (Exception e) {
log.error("创建 Pub/Sub 连接失败", e);
throw new RuntimeException("无法创建 Pub/Sub 连接", e);
}
}
@PreDestroy
public void close() {
if (channel != null) {
channel.shutdown();
try {
if (!channel.awaitTermination(5, TimeUnit.SECONDS)) {
channel.shutdownNow();
}
} catch (InterruptedException e) {
channel.shutdownNow();
}
}
}
}