All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.thestencil.quarkus.ide.FrontendProcessor Maven / Gradle / Ivy

package io.thestencil.quarkus.ide;

/*-
 * #%L
 * quarkus-stencil-ide-deployment
 * %%
 * Copyright (C) 2021 Copyright 2021 ReSys OÜ
 * %%
 * Licensed 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.
 * #L%
 */

import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.Map;

import javax.inject.Inject;

import org.apache.commons.codec.binary.Hex;

import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanContainerListenerBuildItem;
import io.quarkus.bootstrap.model.AppArtifact;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.LiveReloadBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.configuration.ConfigurationError;
import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem;
import io.quarkus.deployment.util.WebJarUtil;
import io.quarkus.vertx.http.deployment.BodyHandlerBuildItem;
import io.quarkus.vertx.http.deployment.HttpRootPathBuildItem;
import io.quarkus.vertx.http.deployment.NonApplicationRootPathBuildItem;
import io.quarkus.vertx.http.deployment.RouteBuildItem;
import io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;

public class FrontendProcessor {
  private static final String WEBJAR_GROUP_ID = "io.thestencil";
  private static final String WEBJAR_ARTIFACT_ID = "stencil-composer-integration";
  private static final String WEBJAR_PREFIX = "META-INF/resources/webjars/" + WEBJAR_ARTIFACT_ID + "/";
  private static final String FINAL_DESTINATION = "META-INF/portal-files";

  
  @Inject
  private LaunchModeBuildItem launch;
  
  FrontendConfig config;
  
  @BuildStep
  FeatureBuildItem feature() {
    return new FeatureBuildItem(FrontendRecorder.FEATURE_BUILD_ITEM);
  }

  @BuildStep
  @Record(ExecutionTime.STATIC_INIT)
  void backendBeans(
      FrontendRecorder recorder,
      BuildProducer buildItems,
      BuildProducer beans) {

    buildItems.produce(AdditionalBeanBuildItem.builder().setUnremovable().addBeanClass(FrontendBeanFactory.class).build());
    beans.produce(new BeanContainerListenerBuildItem(recorder.listener()));
  }
  
  @BuildStep
  @Record(ExecutionTime.RUNTIME_INIT)
  public void frontendHandler(
    FrontendRecorder recorder,
    HttpRootPathBuildItem httpRoot,
    BuildProducer routes,
    HdesUIBuildItem buildItem,
    FrontendConfig uiConfig,
    BodyHandlerBuildItem body) throws Exception {
    
    Handler handler = recorder.uiHandler(buildItem.getUiFinalDestination(), buildItem.getUiPath(), buildItem.getHash());
        routes.produce(httpRoot.routeBuilder()
        .route(uiConfig.servicePath)
        .handler(handler)
        .build());
    routes.produce(httpRoot.routeBuilder()
        .route(uiConfig.servicePath + "/*")
        .handler(handler)
        .build());
  }
  
  @BuildStep
  @Record(ExecutionTime.STATIC_INIT)
  public void frontendBeans(
      FrontendRecorder recorder,
      BuildProducer buildProducer,
      
      BuildProducer generatedResources,
      BuildProducer nativeImage,
      
      NonApplicationRootPathBuildItem nonApplicationRootPathBuildItem,
      CurateOutcomeBuildItem curateOutcomeBuildItem,
      
      LiveReloadBuildItem liveReloadBuildItem,
      HttpRootPathBuildItem httpRootPathBuildItem,
      BuildProducer displayableEndpoints) throws Exception {

    final String hash = Hex.encodeHexString(LocalDateTime.now().toString().getBytes(StandardCharsets.UTF_8), true);
    
    final var artifact = WebJarUtil.getAppArtifact(curateOutcomeBuildItem, WEBJAR_GROUP_ID, WEBJAR_ARTIFACT_ID);    
    if (launch.getLaunchMode().isDevOrTest()) {
      
      Path tempPath = WebJarUtil
          .copyResourcesForDevOrTest(liveReloadBuildItem, curateOutcomeBuildItem, launch, artifact, WEBJAR_PREFIX + artifact.getVersion(), false);
      
      // Update index.html
      Path index = tempPath.resolve("index.html");
      final String frontendPath = httpRootPathBuildItem.resolvePath(config.servicePath);
      
      WebJarUtil.updateFile(index, IndexFactory.builder()
        .frontend(frontendPath)
        .locked(config.locked)
        .oidc(config.oidcPath.orElse(null))
        .status(config.statusPath.orElse(null))
        .server(httpRootPathBuildItem.resolvePath(config.serverPath))
        .index(index)
        .build());
      
      buildProducer.produce(new HdesUIBuildItem(tempPath.toAbsolutePath().toString(), frontendPath, hash));
      displayableEndpoints.produce(new NotFoundPageDisplayableEndpointBuildItem(httpRootPathBuildItem.resolvePath(frontendPath + "/"), "Stencil Composer"));

      // Handle live reload of branding files
      if (liveReloadBuildItem.isLiveReload() && !liveReloadBuildItem.getChangedResources().isEmpty()) {
          WebJarUtil.hotReloadBrandingChanges(
              curateOutcomeBuildItem, launch, artifact,
                  liveReloadBuildItem.getChangedResources());
      }
    
    } else {
      final String frontendPath = httpRootPathBuildItem.resolvePath(config.servicePath);
      Map files = WebJarUtil.copyResourcesForProduction(curateOutcomeBuildItem, artifact, WEBJAR_PREFIX + artifact.getVersion());

      boolean indexReplaced = false; 
      for (Map.Entry file : files.entrySet()) {
        String fileName = file.getKey();
        byte[] content;
        if (fileName.endsWith("index.html")) {
          content = IndexFactory.builder()
              .frontend(frontendPath)
              .locked(config.locked)
              .server(config.serverPath)
              .oidc(config.oidcPath.orElse(null))
              .status(config.statusPath.orElse(null))
              .index(file.getValue())
              .build();
          indexReplaced = true;
        } else {
          content = file.getValue();
        }
        
        fileName = FINAL_DESTINATION + "/" + fileName;
        generatedResources.produce(new GeneratedResourceBuildItem(fileName, content));
        nativeImage.produce(new NativeImageResourceBuildItem(fileName));
      }
      
      if(!indexReplaced) {
        throw new ConfigurationError(new StringBuilder("Failed to create stencil-ide index.html, ")
            .append("artifact = ").append(artifact).append(System.lineSeparator()).append(",")
            .append("path = ").append(frontendPath).append("!")
            .append("final destination = ").append(FINAL_DESTINATION).append("!")
            .toString());
      }
      
      buildProducer.produce(new HdesUIBuildItem(FINAL_DESTINATION, frontendPath, hash));
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy