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

io.quarkiverse.cxf.deployment.Java2WsdlProcessor Maven / Gradle / Ivy

package io.quarkiverse.cxf.deployment;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.stream.Collectors;

import jakarta.jws.WebService;

import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.common.spi.GeneratedClassClassLoaderCapture;
import org.apache.cxf.tools.java2ws.JavaToWS;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.DotName;
import org.jboss.jandex.IndexView;
import org.jboss.logging.Logger;

import io.quarkiverse.cxf.deployment.CxfBuildTimeConfig.Java2WsParameterSet;
import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Consume;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.GeneratedResourceBuildItem;
import io.quarkus.deployment.pkg.builditem.OutputTargetBuildItem;
import io.quarkus.paths.PathFilter;

class Java2WsdlProcessor {

    private static final Logger log = Logger.getLogger(Java2WsdlProcessor.class);

    /*
     * We consume GeneratedBeanBuildItem to ensure that this method is executed after
     * QuarkusCxfProcessor.generateClasses(). Otherwise the wrapper classes might be generated earlier here in this
     * method without capturing to a GeneratedBeanBuildItem. This is because QuarkusCxfProcessor.generateClasses()
     * does not generate classes that are loaded already.
     */
    @Consume(GeneratedBeanBuildItem.class)
    @BuildStep
    void java2wsdl(
            CxfBuildTimeConfig cxfBuildTimeConfig,
            CombinedIndexBuildItem combinedIndex,
            OutputTargetBuildItem outputTargetBuildItem,
            BuildProducer dummy /*
                                                             * Force Quarkus build time container to call this method
                                                             * even though it does not produce any BuildItems
                                                             */) {

        final String targetDir = outputTargetBuildItem.getOutputDirectory().toString();
        final String classesDir = outputTargetBuildItem.getOutputDirectory().resolve("classes").toString();

        final IndexView index = combinedIndex.getIndex();
        if (!cxfBuildTimeConfig.java2ws().enabled()) {
            log.info("Skipping " + this.getClass() + " invocation on user's request");
            return;
        }

        final Set services = index.getAnnotations(DotName.createSimple(WebService.class.getName()))
                .stream()
                .map(AnnotationInstance::target)
                .filter(annotationTarget -> annotationTarget.kind() == AnnotationTarget.Kind.CLASS)
                .map(AnnotationTarget::asClass)
                .map(classInfo -> classInfo.name())
                .collect(Collectors.toCollection(TreeSet::new));

        final Bus oldBus = BusFactory.getThreadDefaultBus();
        final Bus java2wsdlBus = BusFactory.newInstance().createBus();
        java2wsdlBus.setExtension(null, GeneratedClassClassLoaderCapture.class);
        BusFactory.setThreadDefaultBus(java2wsdlBus);
        try {
            final Map processedClasses = new LinkedHashMap<>();
            java2wsdl(
                    services,
                    cxfBuildTimeConfig.java2ws().rootParameterSet(),
                    Java2WsParameterSet.JAVA2WS_CONFIG_KEY_PREFIX,
                    targetDir,
                    classesDir,
                    processedClasses);

            // named
            for (Entry en : cxfBuildTimeConfig.java2ws().namedParameterSets().entrySet()) {
                java2wsdl(
                        services,
                        en.getValue(),
                        Java2WsParameterSet.JAVA2WS_CONFIG_KEY_PREFIX + "." + en.getKey(),
                        targetDir,
                        classesDir,
                        processedClasses);
            }
            log.infof("java2ws processed %d classes", processedClasses.size());
        } finally {
            BusFactory.setThreadDefaultBus(oldBus);
        }
    }

    static void java2wsdl(
            Set serviceClasses,
            CxfBuildTimeConfig.Java2WsParameterSet params,
            String prefix,
            String targetDir,
            String classesDir,
            Map processedClasses) {
        params.validate(prefix);
        if (params.includes().isEmpty()) {
            /* Nothing to do */
            return;
        }
        final String selectors = "    " + prefix + ".includes = " + params.includes().get() +
                (params.excludes().isPresent()
                        ? "\n    " + prefix + ".excludes = " + params.excludes().get()
                        : "");

        final PathFilter pathFilter = new PathFilter(toSlashNames(params.includes()), toSlashNames(params.excludes()));

        for (DotName serviceClass : serviceClasses) {
            if (pathFilter.isVisible(serviceClass.toString('/'))) {
                final String oldSelectors = processedClasses.get(serviceClass.toString());
                if (oldSelectors != null) {
                    throw new IllegalStateException("Service class " + serviceClass + " was already selected by\n\n"
                            + oldSelectors
                            + "\n\nand therefore it cannot once again be selected by\n\n" + selectors
                            + "\n\nPlease make sure that the individual include/exclude sets are mutually exclusive.");
                }

                processedClasses.put(serviceClass.toString(), selectors);
                final Java2WsdlParams java2WsdlParams = new Java2WsdlParams(
                        serviceClass.toString(),
                        params.additionalParams().orElse(Collections.emptyList()),
                        generateWsdlName(params.wsdlNameTemplate(), serviceClass, targetDir, classesDir));
                if (log.isInfoEnabled()) {
                    log.info(java2WsdlParams.appendLog(new StringBuilder("Running java2ws")).toString());
                }

                try {
                    new JavaToWS(java2WsdlParams.toParameterArray()).run();
                } catch (Exception e) {
                    throw new RuntimeException(
                            java2WsdlParams.appendLog(new StringBuilder("Could not run java2ws")).toString(),
                            e);
                }
            }
        }
    }

    static List toSlashNames(Optional> includes) {
        if (includes.isPresent()) {
            return includes.get().stream().map(incl -> incl.replace('.', '/')).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    static String generateWsdlName(String template, DotName serviceClass, String targetDir, String classesDir) {
        return template
                .replace("%SIMPLE_CLASS_NAME%", serviceClass.withoutPackagePrefix())
                .replace("%FULLY_QUALIFIED_CLASS_NAME%", serviceClass.toString().replace('.', '_'))
                .replace("%TARGET_DIR%", targetDir)
                .replace("%CLASSES_DIR%", classesDir);
    }

    static class Java2WsdlParams {
        private final String serviceClassName;
        private final List additionalParams;
        private final String outputPath;

        Java2WsdlParams(String serviceClassName, List additionalParams, String outputPath) {
            super();
            this.serviceClassName = serviceClassName;
            this.additionalParams = additionalParams;
            this.outputPath = outputPath;
        }

        StringBuilder appendLog(StringBuilder sb) {
            render(value -> sb.append(' ').append(value));
            return sb;
        }

        String[] toParameterArray() {
            final String[] result = new String[additionalParams.size() + 4];
            final AtomicInteger i = new AtomicInteger(0);
            render(value -> result[i.getAndIncrement()] = value);
            return result;
        }

        void render(Consumer paramConsumer) {
            paramConsumer.accept("-wsdl");
            paramConsumer.accept("-o");
            paramConsumer.accept(outputPath);
            additionalParams.forEach(paramConsumer);
            paramConsumer.accept(serviceClassName);
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy