メインコンテンツまでスキップ
バージョン: 2.0

Java プロジェクトを Reveal SDK 2.0 へ移行する

このガイドでは、com.infragistics.reveal.sdk:reveal-sdk 1.x を使用している Spring Boot + Jersey プロジェクトを、サーブレット ベースの io.revealbi:reveal-sdk-servlet 2.0 パッケージへ移行する方法を説明します。プレーンなサーブレット コンテナーでも SDK の変更点は同じで、異なるのはアプリケーションの起動方法だけです。

備考

Java 向け Reveal SDK 2.0 には Java 17 以降 と Jakarta EE 9 準拠サーバーが必要です。

変更点

Reveal SDK 1.xReveal SDK 2.0
com.infragistics.reveal.sdk:reveal-sdk Java 専用エンジンio.revealbi:reveal-sdk-servlet ネイティブ RevealEnginePrg サイドカーを使う Java プロキシ
RevealEngineInitializer.getClassesToRegister() を使う Jersey JAX-RS 登録通常のサーブレットと同様に RevealEngineServlet を 1 つ登録
com.infragistics.reveal.sdk.api.* パッケージio.revealbi.core.* および io.revealbi.core.data.* パッケージ
Jersey リクエスト コンテキスト向け RVContainerRequestAwareUserContextProviderサーブレット リクエスト コンテキスト向け IRVServletUserContextProvider
Java 11 以降Java 17 以降
Java プロセス内エンジンJava プロキシがネイティブ Reveal エンジンを子プロセスとして起動

移行手順

  1. Maven 依存関係と Java バージョンを更新します。
  2. Jersey 登録を RevealEngineServlet に置き換えます。
  3. Jersey のユーザー コンテキスト プロバイダーを IRVServletUserContextProvider に置き換えます。
  4. JAX-RS の CORS フィルターとカスタム リソースをサーブレット フィルターまたは Spring MVC に変換します。
  5. プロバイダーの import を更新し、削除された API オーバーロードを取り除きます。
  6. アプリケーションをビルドし、実際の Reveal JavaScript クライアント経由でテストします。

ステップ 1 - Maven 依存関係を更新する

1.x の SDK 依存関係を 2.0 のサーブレット パッケージに置き換えます。

pom.xml
<dependency>
<groupId>com.infragistics.reveal.sdk</groupId>
<artifactId>reveal-sdk</artifactId>
<version>1.8.0</version>
</dependency>

アプリケーションに Reveal 以外の Jersey コードが残っていない限り、spring-boot-starter-jerseyprovided スコープの spring-boot-starter-tomcat 依存関係は削除してください。spring-boot-starter-web は維持します。

Reveal Maven リポジトリを追加します。

pom.xml
<repositories>
<repository>
<id>reveal.public</id>
<url>https://maven.revealbi.io/repository/public</url>
</repository>
<repository>
<id>reveal.release-stage</id>
<url>https://maven.revealbi.io/repository/release-stage</url>
<releases><enabled>true</enabled></releases>
</repository>
</repositories>

Maven の Java バージョンを 17 以降に更新します。

pom.xml
<properties>
<java.version>17</java.version>
</properties>

ステップ 2 - Jersey ブートストラップを RevealEngineServlet に置き換える

Reveal を初期化して Reveal の JAX-RS クラスを登録していた Jersey ResourceConfig を削除し、代わりに Spring Boot アプリケーションから RevealEngineServlet を登録します。

RevealJerseyConfig.java
@Component
@ApplicationPath("/")
public class RevealJerseyConfig extends ResourceConfig {
@Autowired
public void configureReveal(AuthenticationProvider authProvider, ...) {
RevealEngineInitializer.initialize(new InitializeParameterBuilder()
.setAuthProvider(authProvider)
.setDashboardProvider(dashboardProvider)
.setDataSourceProvider(dsProvider)
.setUserContextProvider(userContextProvider)
.setObjectFilter(objectFilter)
.build());

for (Class<?> clazz : RevealEngineInitializer.getClassesToRegister()) {
register(clazz);
}
register(CorsFilter.class);
}
}
注意

setAsyncSupported(true) は必須です。サーブレットは RevealEnginePrg へリクエストを転送する際に request.startAsync() を使用します。

サーブレットのマウント パスを選ぶ

パターン使用する場面クライアント base URL
Reveal を /* にマウントサーバーが Reveal SDK エンドポイントのみを提供するhttp://host:port/
Reveal を /reveal-api/* のようなサブパスにマウントサーバーがルートで Spring MVC コントローラーや静的ファイルも提供するhttp://host:port/reveal-api/
Reveal を内部パスでマウントし、ルートの未一致リクエストをそこへルーティングクライアントの base URL をルートのままにしたいが、サーバーは Reveal 以外のエンドポイントも公開するhttp://host:port/

サーブレット仕様では、リクエスト パスは 1 つのサーブレットにのみルーティングされます。Reveal を /* にマウントすると、/ にマップされた Spring MVC コントローラーと静的リソース ハンドラーはそのリクエストを受け取れません。

内部ルーティング パターンでは、Reveal を /__reveal__/* のようなプライベート パスにマウントし、未一致リクエストを内部 Reveal パスへ転送する Spring OncePerRequestFilter を追加します。

RevealRoutingFilter.java
public class RevealRoutingFilter extends OncePerRequestFilter {
static final String REVEAL_INTERNAL_PREFIX = "/__reveal__";
private final RequestMappingHandlerMapping handlerMapping;
private final Path clientFolder;

public RevealRoutingFilter(RequestMappingHandlerMapping handlerMapping, Path clientFolder) {
this.handlerMapping = handlerMapping;
this.clientFolder = clientFolder;
}

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
String path = request.getRequestURI();
if (path.startsWith(REVEAL_INTERNAL_PREFIX)
|| controllerCanHandle(request)
|| clientFileExists(path)) {
chain.doFilter(request, response);
} else {
request.getRequestDispatcher(REVEAL_INTERNAL_PREFIX + path).forward(request, response);
}
}

@Override
protected boolean shouldNotFilterAsyncDispatch() {
return false;
}

private boolean controllerCanHandle(HttpServletRequest request) {
try {
return handlerMapping.getHandler(request) != null;
} catch (Exception e) {
return false;
}
}

private boolean clientFileExists(String path) {
String relativePath = path.equals("/") ? "index.html" : path.substring(1);
Path candidate = clientFolder.resolve(relativePath).normalize();
return candidate.startsWith(clientFolder)
&& Files.exists(candidate)
&& Files.isRegularFile(candidate);
}
}

ルーティング フィルターの登録時にも async サポートを有効にします。GET /index.html を返す場合は、Spring のビュー コントローラーを追加します。

registry.addViewController("/").setViewName("forward:/index.html");

ステップ 3 - ユーザー コンテキストを更新する

RVContainerRequestAwareUserContextProvider は Jersey 専用であり、現在は使用できません。生の HttpServletRequest を受け取る io.revealbi.servlet.IRVServletUserContextProvider を使用します。

UserContextProvider.java
@Component
public class UserContextProvider extends RVContainerRequestAwareUserContextProvider {
@Override
protected IRVUserContext getUserContext(ContainerRequestContext context) {
String header = context.getHeaderString("x-header-one");
...
return new RVUserContext(userId, properties);
}
}

ユーザー コンテキスト プロバイダーは RevealEngineServlet コンストラクターの第 2 引数として渡します。ラムダを渡すこともできます。

new RevealEngineServlet(server, request -> new RVUserContext("anonymous", propertiesFor(request)));

ステップ 4 - CORS をサーブレット フィルターに変換する

Jersey の ContainerRequestFilterContainerResponseFilter は Reveal SDK リクエストには適用されません。CORS の処理は jakarta.servlet.Filter に変換してください。

PermissiveCorsFilter.java
@Component
public class PermissiveCorsFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String origin = httpRequest.getHeader("Origin");

httpResponse.setHeader("Access-Control-Allow-Origin", origin == null ? "*" : origin);
httpResponse.setHeader("Vary", "Origin");
httpResponse.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
httpResponse.setHeader("Access-Control-Allow-Headers",
"X-Requested-With, Authorization, Content-Type, Cache-Control, Pragma");
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Max-Age", "3600");

if ("OPTIONS".equalsIgnoreCase(httpRequest.getMethod())) {
httpResponse.setStatus(HttpServletResponse.SC_NO_CONTENT);
return;
}

chain.doFilter(request, response);
}
}

Spring Boot では、@Component のサーブレット フィルターはすべての URL に自動登録されます。

ステップ 5 - プロバイダーの import を更新する

com.infragistics.reveal.sdk.api.* の import はすべて io.revealbi.core.* または io.revealbi.core.data.* に移動します。クラス名の多くはそのままです。

Reveal SDK 1.xReveal SDK 2.0
com.infragistics.reveal.sdk.api.IRVUserContextio.revealbi.core.IRVUserContext
com.infragistics.reveal.sdk.base.RVUserContextio.revealbi.core.RVUserContext
com.infragistics.reveal.sdk.api.IRVDashboardProviderio.revealbi.core.IRVDashboardProvider
com.infragistics.reveal.sdk.api.IRVAuthenticationProviderio.revealbi.core.data.IRVAuthenticationProvider
com.infragistics.reveal.sdk.api.IRVDataSourceCredentialio.revealbi.core.data.IRVDataSourceCredential
com.infragistics.reveal.sdk.api.RVUsernamePasswordDataSourceCredentialio.revealbi.core.data.RVUsernamePasswordDataSourceCredential
com.infragistics.reveal.sdk.api.IRVDataSourceProviderio.revealbi.core.data.IRVDataSourceProvider
com.infragistics.reveal.sdk.api.IRVObjectFilterio.revealbi.core.data.IRVObjectFilter
com.infragistics.reveal.sdk.api.model.RV*DataSource*io.revealbi.core.data.RV*DataSource*

同梱のファイルシステム ダッシュボード プロバイダーは io.revealbi.core.RVDashboardProvider になりました。コンストラクターには 絶対パス が必要です。相対パスを渡すと IllegalArgumentException が発生します。

new RVDashboardProvider(Paths.get("Dashboards").toAbsolutePath().toString())

ステップ 6 - 削除された API を取り除く

以下の API は Reveal SDK 2.0 では利用できません。

  • IRVRestUrlResolver
  • IRVDashboardAuthorizationProvider
  • IRVAuthenticationResolver
  • IRVObjectEncoder
  • RVContainerRequestAwareUserContextProvider
  • IRVObjectFilter.filter(IRVUserContext, RVDashboardDataSource) オーバーロード

IRVObjectFilter は、データ ソース アイテム向けオーバーロードのみ残します。

ObjectFilter.java
@Component
public class ObjectFilter implements IRVObjectFilter {
@Override
public boolean filter(IRVUserContext userContext, RVDataSourceItem item) {
...
}
}

ステップ 7 - JAX-RS コントローラーを Spring MVC に変換する

Jersey 構成で Reveal と一緒にカスタム リソースを登録していた場合、それらを Spring MVC コントローラーへ変換します。

@Path("/dashboards")
public class DomController {
@GET
@Path("/names")
@Produces(MediaType.APPLICATION_JSON)
public List<DashboardInfo> getNames() {
...
}
}

JAX-RS の ContainerRequestContext.getHeaderString()HttpServletRequest.getHeader() に置き換えます。JAX-RS インターセプターはサーブレット フィルターまたは Spring の HandlerInterceptor 実装へ変換してください。

ステップ 8 - 移行を確認する

アプリケーション起動後、ログにはコールバック サーバーと Spring Boot アプリケーションが表示されるはずです。

... org.eclipse.jetty.server.Server : Started oejs.Server@...{...0.0.0.0:<callback-port>}
... TomcatWebServer : Tomcat started on port <your-port>
... Started <YourApp> in N seconds

RevealEnginePrg.exe(macOS/Linux ではネイティブ バイナリ)は最初の Reveal リクエスト時に遅延起動されます。Windows では %TEMP%/revealbi-logs/reveal-engine.log にエンジン ログを書き込みます。

最初のアクセス成功時、エンジン ログには次のような内容が出力されます。

INFO|Reveal.Sdk.Global|License read from file.
INFO|Microsoft.AspNetCore.Hosting.Diagnostics|Request starting HTTP/1.1 GET http://127.0.0.1:<engine-port>/dashboards/...

JavaScript クライアントで RevealSdkSettings.setBaseUrl をサーブレットのマウント先に向け、ダッシュボードをロードして配線を確認してください。/dashboards/{id}/exists のような 1.x の旧 URL を疎通確認に使わないでください。

よくある落とし穴

症状原因と対処
Spring MVC コントローラーや静的ファイルが応答しなくなるReveal を /* にマウントしているためです。サブパスにマウントするか、Spring MVC をサブパスへ移動するか、内部ルーティング フィルターを追加してください。
ルーティング フィルターで転送後にリクエストがハングするフィルター、内部 Reveal サーブレット、ディスパッチ タイプのすべてで async サポートが必要です。フィルターとサーブレットで setAsyncSupported(true) を設定し、shouldNotFilterAsyncDispatch()false を返してください。
起動時に AutowiredAnnotationBeanPostProcessor のトレースで失敗する必須の @Value("${SOMETHING}") プロパティが不足しています。プロパティを追加するか、${SOMETHING:fallback} のような既定値を設定してください。
初回リクエストで Connection timed out が出るネイティブ エンジンの起動が完了していません。再試行し、継続する場合はエンジン ログを確認してください。
macOS arm64 で 403 や署名エラーが出るcodesign -s - <path-to-RevealEnginePrg> でネイティブ バイナリに一度 ad-hoc 署名してください。
誤ったネイティブ バイナリがパッケージされるビルド環境と異なる OS/アーキテクチャ向けにパッケージする場合、-P linux-x86_64 のように対象 Maven プロファイルを指定してください。
シャットダウン後も RevealEnginePrg が残るJava 親プロセスが子プロセスを終了するのは、mvn spring-boot:run での Ctrl+C や Spring ContextClosedEvent のような正常終了時のみです。
ログ出力が変わったJava SDK は SLF4J を使用します。ネイティブ エンジンは SLF4J 設定ではなく専用ログ ファイルへ出力します。

参考サンプル