content: /*
   * 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.camel.quarkus.main;
  
  import java.io.IOException;
  import java.net.ConnectException;
  import java.net.Socket;
  import java.nio.charset.StandardCharsets;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Objects;
  
  import jakarta.enterprise.context.ApplicationScoped;
  import jakarta.inject.Inject;
  import jakarta.json.Json;
  import jakarta.json.JsonArrayBuilder;
  import jakarta.json.JsonObject;
  import jakarta.json.JsonObjectBuilder;
  import jakarta.ws.rs.GET;
  import jakarta.ws.rs.POST;
  import jakarta.ws.rs.Path;
  import jakarta.ws.rs.PathParam;
  import jakarta.ws.rs.Produces;
  import jakarta.ws.rs.core.MediaType;
  import org.apache.camel.CamelContext;
  import org.apache.camel.Component;
  import org.apache.camel.Route;
  import org.apache.camel.ServiceStatus;
  import org.apache.camel.component.log.LogComponent;
  import org.apache.camel.impl.debugger.DebuggerJmxConnectorService;
  import org.apache.camel.model.ModelCamelContext;
  import org.apache.camel.quarkus.core.CamelRuntime;
  import org.apache.camel.quarkus.core.FastFactoryFinderResolver;
  import org.apache.camel.quarkus.it.support.typeconverter.MyPair;
  import org.apache.camel.reactive.vertx.VertXReactiveExecutor;
  import org.apache.camel.reactive.vertx.VertXThreadPoolFactory;
  import org.apache.camel.spi.BeanRepository;
  import org.apache.camel.spi.ContextReloadStrategy;
  import org.apache.camel.spi.DataFormat;
  import org.apache.camel.spi.FactoryFinderResolver;
  import org.apache.camel.spi.Language;
  import org.apache.camel.spi.ReactiveExecutor;
  import org.apache.camel.spi.Resource;
  import org.apache.camel.spi.ThreadPoolFactory;
  import org.apache.camel.support.DefaultRegistry;
  import org.apache.camel.support.LRUCacheFactory;
  import org.apache.camel.support.PluginHelper;
  import org.apache.camel.support.processor.DefaultExchangeFormatter;
  
  @Path("/test")
  @ApplicationScoped
  public class CoreMainResource {
      @Inject
      CamelMain main;
  
      @Inject
      CamelRuntime camelRuntime;
  
      @Path("/property/{name}")
      @GET
      @Produces(MediaType.TEXT_PLAIN)
      public String getProperty(@PathParam("name") String name) throws Exception {
          return main.getCamelContext().resolvePropertyPlaceholders("{{" + name + "}}");
      }
  
      @Path("/registry/log/exchange-formatter")
      @GET
      @Produces(MediaType.APPLICATION_JSON)
      public JsonObject exchangeFormatterConfig() {
          LogComponent component = main.getCamelContext().getRegistry().lookupByNameAndType("log", LogComponent.class);
          DefaultExchangeFormatter def = (DefaultExchangeFormatter) component.getExchangeFormatter();
  
          JsonObject result = Json.createObjectBuilder()
                  .add("show-all", def.isShowAll())
                  .add("multi-line", def.isMultiline())
                  .build();
  
          return result;
      }
  
      @Path("/context/name")
      @GET
      @Produces(MediaType.TEXT_PLAIN)
      public String getCamelContextName() {
          return main.getCamelContext().getName();
      }
  
      @Path("/context/name")
      @POST
      @Produces(MediaType.TEXT_PLAIN)
      public String setCamelContextName(String name) {
          main.getCamelContext().getCamelContextExtension().setName(name);
          return main.getCamelContext().getName();
      }
  
      @Path("/main/describe")
      @GET
      @Produces(MediaType.APPLICATION_JSON)
      public JsonObject describeMain() {
          CamelContext camelContext = main.getCamelContext();
  
          JsonArrayBuilder routeBuilders = Json.createArrayBuilder();
          main.configure().getRoutesBuilders().forEach(builder -> routeBuilders.add(builder.getClass().getName()));
  
          JsonArrayBuilder routes = Json.createArrayBuilder();
          camelContext.getRoutes().forEach(route -> routes.add(route.getId()));
  
          JsonObjectBuilder collector = Json.createObjectBuilder();
          collector.add("type", main.getRoutesCollector().getClass().getName());
          if (main.getRoutesCollector() instanceof CamelMainRoutesCollector) {
              CamelMainRoutesCollector crc = (CamelMainRoutesCollector) main.getRoutesCollector();
              collector.add("type-registry", crc.getRegistryRoutesLoader().getClass().getName());
          }
  
          JsonObjectBuilder dataformatsInRegistry = Json.createObjectBuilder();
          camelContext.getRegistry().findByTypeWithName(DataFormat.class)
                  .forEach((name, value) -> dataformatsInRegistry.add(name, value.getClass().getName()));
  
          JsonObjectBuilder languagesInRegistry = Json.createObjectBuilder();
          camelContext.getRegistry().findByTypeWithName(Language.class)
                  .forEach((name, value) -> languagesInRegistry.add(name, value.getClass().getName()));
  
          JsonObjectBuilder componentsInRegistry = Json.createObjectBuilder();
          camelContext.getRegistry().findByTypeWithName(Component.class)
                  .forEach((name, value) -> componentsInRegistry.add(name, value.getClass().getName()));
  
          JsonObjectBuilder factoryClassMap = Json.createObjectBuilder();
          FactoryFinderResolver factoryFinderResolver = PluginHelper.getFactoryFinderResolver(camelContext);
          if (factoryFinderResolver instanceof FastFactoryFinderResolver) {
              ((FastFactoryFinderResolver) factoryFinderResolver).getClassMap().forEach((k, v) -> {
                  factoryClassMap.add(k, v.getName());
              });
          }
  
          return Json.createObjectBuilder()
                  .add("xml-model-dumper", PluginHelper.getModelToXMLDumper(camelContext).getClass().getName())
                  .add("routes-collector", collector)
                  .add("routeBuilders", routeBuilders)
                  .add("routes", routes)
                  .add("lru-cache-factory", LRUCacheFactory.getInstance().getClass().getName())
                  .add("autoConfigurationLogSummary", main.getMainConfigurationProperties().isAutoConfigurationLogSummary())
                  .add("config", Json.createObjectBuilder()
                          .add("rest-port",
                                  camelContext.getRestConfiguration().getPort())
                          .add("resilience4j-sliding-window-size",
                                  ((ModelCamelContext) camelContext)
                                          .getResilience4jConfiguration(null)
                                          .getSlidingWindowSize()))
                  .add("registry", Json.createObjectBuilder()
                          .add("components", componentsInRegistry)
                          .add("dataformats", dataformatsInRegistry)
                          .add("languages", languagesInRegistry))
                  .add("factory-finder", Json.createObjectBuilder()
                          .add("class-map", factoryClassMap))
                  .add("bean-introspection-invocations",
                          PluginHelper.getBeanIntrospection(camelContext).getInvokedCounter())
                  .build();
      }
  
      @Path("/context/reactive-executor")
      @GET
      @Produces(MediaType.TEXT_PLAIN)
      public JsonObject reactiveExecutor() {
          ReactiveExecutor executor = main.getCamelContext().getCamelContextExtension().getReactiveExecutor();
  
          JsonObjectBuilder builder = Json.createObjectBuilder();
          builder.add("class", executor.getClass().getName());
  
          if (executor instanceof VertXReactiveExecutor) {
              builder.add("configured", ((VertXReactiveExecutor) executor).getVertx() != null);
  
          }
  
          return builder.build();
      }
  
      @Path("/context/thread-pool-factory")
      @GET
      @Produces(MediaType.TEXT_PLAIN)
      public JsonObject threadPoolFactory() {
          ThreadPoolFactory threadPoolFactory = main.getCamelContext().getExecutorServiceManager().getThreadPoolFactory();
  
          JsonObjectBuilder builder = Json.createObjectBuilder();
          builder.add("class", threadPoolFactory.getClass().getName());
  
          if (threadPoolFactory instanceof VertXThreadPoolFactory) {
              builder.add("configured", ((VertXThreadPoolFactory) threadPoolFactory).getVertx() != null);
          }
  
          return builder.build();
      }
  
      @Path("/converter/my-pair")
      @POST
      @Produces(MediaType.APPLICATION_JSON)
      public JsonObject fromStringToMyPair(String input) {
          MyPair pair = main.getCamelContext().getTypeConverter().convertTo(MyPair.class, input);
  
          return Json.createObjectBuilder()
                  .add("key", pair.key)
                  .add("val", pair.val)
                  .build();
      }
  
      @Path("/registry/component/{name}")
      @GET
      @Produces(MediaType.APPLICATION_JSON)
      public JsonObject describeRegistryComponent(@PathParam("name") String name) {
          final Map<String, Object> properties = new HashMap<>();
          final DefaultRegistry registry = main.getCamelContext().getRegistry(DefaultRegistry.class);
          final JsonObjectBuilder builder = Json.createObjectBuilder();
  
          Component component = registry.getFallbackRegistry().lookupByNameAndType(name, Component.class);
          if (component != null) {
              builder.add("type", component.getClass().getName());
              builder.add("registry", "fallback");
              builder.add("registry-type", registry.getFallbackRegistry().getClass().getName());
          } else {
              for (BeanRepository repository : registry.getRepositories()) {
                  component = repository.lookupByNameAndType(name, Component.class);
                  if (component != null) {
                      builder.add("type", component.getClass().getName());
                      builder.add("registry", "repository");
                      builder.add("registry-type", repository.getClass().getName());
                      break;
                  }
              }
          }
  
          if (component != null) {
              PluginHelper.getBeanIntrospection(main.getCamelContext()).getProperties(component, properties,
                      null);
              properties.forEach((k, v) -> {
                  if (v != null) {
                      builder.add(k, Objects.toString(v));
                  }
              });
          }
  
          return builder.build();
      }
  
      @Path("/registry/string/{name}")
      @GET
      @Produces(MediaType.TEXT_PLAIN)
      public String getStringValueFromRegistry(@PathParam("name") String name) {
          final DefaultRegistry registry = main.getCamelContext().getRegistry(DefaultRegistry.class);
          return registry.getFallbackRegistry().lookupByNameAndType(name, String.class);
      }
  
      @Path("/service/jmx-connector/status")
      @GET
      @Produces(MediaType.TEXT_PLAIN)
      public String getJmxConnectorServiceStatus() {
          DebuggerJmxConnectorService service = main.getCamelContext().hasService(DebuggerJmxConnectorService.class);
          if (service == null) {
              return ServiceStatus.Stopped.name().toUpperCase();
          }
          return service.getStatus().name().toUpperCase();
      }
  
      @Path("/service/jmx-connector/expected/connect/state")
      @GET
      @Produces(MediaType.TEXT_PLAIN)
      public boolean jmxConnectorServiceAvailabilityIsExpectedState() {
          boolean isNativeMode = "executable".equals(System.getProperty("org.graalvm.nativeimage.kind"));
          try (Socket socket = new Socket("localhost", DebuggerJmxConnectorService.DEFAULT_REGISTRY_PORT)) {
              return true;
          } catch (IOException e) {
              // Since camel-quarkus-management is not on the classpath
              // native mode will replace DebuggerJmxConnectorService methods with noop implementations
              // hence the expected state is that we should not be able to connect
              return isNativeMode && e instanceof ConnectException;
          }
      }
  
      @Path("/context/reload/strategy")
      @GET
      @Produces(MediaType.TEXT_PLAIN)
      public String contextReloadStrategy() {
          ContextReloadStrategy contextReloadStrategy = main.getCamelContext().hasService(ContextReloadStrategy.class);
          if (contextReloadStrategy != null) {
              return contextReloadStrategy.getClass().getName();
          }
          return null;
      }
  
      @Path("/context/reload")
      @GET
      @Produces(MediaType.TEXT_PLAIN)
      public boolean contextReloadStatus() {
          return ContextReloadObserver.contextReloaded.get();
      }
  
      @Path("/context/reload")
      @POST
      public void contextReload() {
          ContextReloadStrategy contextReloadStrategy = main.getCamelContext().hasService(ContextReloadStrategy.class);
          if (contextReloadStrategy != null) {
              contextReloadStrategy.onReload(this);
          }
      }
  
      @Path("/runtime/status")
      @GET
      @Produces(MediaType.TEXT_PLAIN)
      public String runtimeStatus() {
          return main.getStatus().name();
      }
  
      @Path("/runtime/start")
      @POST
      public void runtimeStart() {
          camelRuntime.start();
      }
  
      @Path("/context/route/source/resource")
      @GET
      @Produces(MediaType.TEXT_PLAIN)
      public String contextRouteSourceResource() throws IOException {
          Route route = main.getCamelContext().getRoute("camel-cdi-bean-route");
          Resource sourceResource = route.getSourceResource();
          if (sourceResource != null && sourceResource.exists()) {
              return new String(sourceResource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
          }
          return null;
      }
  }
  