Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ quarkus.cxf.client.clientWithRuntimeInitializedPayload.service-interface = io.qu
quarkus.cxf.client.clientWithRuntimeInitializedPayload.endpoint-namespace = http://www.jboss.org/eap/quickstarts/wscalculator/Calculator
quarkus.cxf.client.clientWithRuntimeInitializedPayload.endpoint-name = CalculatorService
quarkus.cxf.client.clientWithRuntimeInitializedPayload.native.runtime-initialized = true
quarkus.native.additional-build-args = --initialize-at-run-time=io.quarkiverse.cxf.client.it.rtinit.Operands\\,io.quarkiverse.cxf.client.it.rtinit.Result
quarkus.native.additional-build-args = -H:+UseNewExperimentalClassInitialization,--initialize-at-run-time=io.quarkiverse.cxf.client.it.rtinit.Operands\\,io.quarkiverse.cxf.client.it.rtinit.Result

quarkus.cxf.codegen.wsdl2java.includes = wsdl/*.wsdl
2 changes: 1 addition & 1 deletion docs/modules/ROOT/examples/calculator-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<parent>
<groupId>io.quarkiverse.cxf</groupId>
<artifactId>quarkus-cxf-integration-tests</artifactId>
<version>2.2.0-SNAPSHOT</version>
<version>2.2.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,15 @@
*/
public final class CxfClientBuildItem extends AbstractEndpointBuildItem {
private final String sei;
private final boolean proxyClassRuntimeInitialized;

public CxfClientBuildItem(String sei, String soapBinding, String wsNamespace,
String wsName, boolean runtimeInitialized) {
String wsName) {
super(soapBinding, wsNamespace, wsName);
this.sei = sei;
this.proxyClassRuntimeInitialized = runtimeInitialized;
}

public String getSei() {
return sei;
}

public boolean isProxyClassRuntimeInitialized() {
return proxyClassRuntimeInitialized;
}

}
Original file line number Diff line number Diff line change
@@ -1,27 +1,20 @@
package io.quarkiverse.cxf.deployment;

import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
import jakarta.enterprise.inject.spi.InjectionPoint;
import jakarta.inject.Inject;
import jakarta.xml.ws.BindingProvider;
import jakarta.xml.ws.soap.SOAPBinding;

import org.apache.cxf.endpoint.Client;
import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationValue;
Expand Down Expand Up @@ -54,9 +47,6 @@
import io.quarkus.deployment.builditem.NativeImageFeatureBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedPackageBuildItem;
import io.quarkus.deployment.util.IoUtil;
import io.quarkus.gizmo.ClassCreator;
import io.quarkus.gizmo.ClassOutput;
import io.quarkus.gizmo.FieldCreator;
Expand All @@ -75,22 +65,12 @@ public class CxfClientProcessor {
void collectClients(
CxfFixedConfig config,
CombinedIndexBuildItem combinedIndexBuildItem,
List<RuntimeInitializedClassBuildItem> runtimeInitializedClasses,
List<RuntimeInitializedPackageBuildItem> runtimeInitializedPackages,
BuildProducer<NativeImageFeatureBuildItem> nativeImageFeatures,
BuildProducer<NativeImageProxyDefinitionBuildItem> proxies,
BuildProducer<CxfClientBuildItem> clients,
BuildProducer<ClientSeiBuildItem> clientSeis) {
IndexView index = combinedIndexBuildItem.getIndex();

final Set<String> rtInitClasses = runtimeInitializedClasses.stream()
.map(RuntimeInitializedClassBuildItem::getClassName)
.collect(Collectors.toSet());
final Set<String> rtInitPackages = runtimeInitializedPackages.stream()
.map(RuntimeInitializedPackageBuildItem::getPackageName)
.collect(Collectors.toSet());

final AtomicBoolean hasRuntimeInitializedProxy = new AtomicBoolean(false);
final Map<String, ClientFixedConfig> clientSEIsInUse = findClientSEIsInUse(index, config);
CxfDeploymentUtils.webServiceAnnotations(index)
.forEach(annotation -> {
Expand All @@ -117,28 +97,15 @@ void collectClients(
.map(bindingType -> bindingType.value().asString())
.orElse(SOAPBinding.SOAP11HTTP_BINDING);

final ProxyInfo proxyInfo = ProxyInfo.of(
Optional.ofNullable(clientConfig.native_).map(native_ -> native_.runtimeInitialized)
.orElse(false),
wsClassInfo,
rtInitClasses,
rtInitPackages,
index);
proxies.produce(new NativeImageProxyDefinitionBuildItem(proxyInfo.interfaces));

clients.produce(
new CxfClientBuildItem(sei, soapBinding, wsNamespace, wsName, proxyInfo.isRuntimeInitialized));
new CxfClientBuildItem(sei, soapBinding, wsNamespace, wsName));
clientSeis.produce(new ClientSeiBuildItem(sei));

hasRuntimeInitializedProxy.set(hasRuntimeInitializedProxy.get() || proxyInfo.isRuntimeInitialized);

proxies.produce(new NativeImageProxyDefinitionBuildItem(
sei, "jakarta.xml.ws.BindingProvider", "java.io.Closeable", "org.apache.cxf.endpoint.Client"));
}
});

if (hasRuntimeInitializedProxy.get()) {
nativeImageFeatures.produce(new NativeImageFeatureBuildItem(QuarkusCxfFeature.class));
}

nativeImageFeatures.produce(new NativeImageFeatureBuildItem(QuarkusCxfFeature.class));
}

@BuildStep
Expand All @@ -158,8 +125,7 @@ void startClient(
client.getSoapBinding(),
client.getSei(),
client.getWsName(),
client.getWsNamespace(),
client.isProxyClassRuntimeInitialized()))
client.getWsNamespace()))
.map(cxf -> {
LOGGER.debugf("producing dedicated CXFClientInfo bean named '%s' for SEI %s", cxf.getSei(), cxf.getSei());
return SyntheticBeanBuildItem
Expand Down Expand Up @@ -285,38 +251,6 @@ void generateClientProducers(
.forEach(sei -> {
generateCxfClientProducer(sei, generatedBeans, unremovableBeans);
});

if (clients.stream().anyMatch(CxfClientBuildItem::isProxyClassRuntimeInitialized)) {
reflectiveClasses
.produce(ReflectiveClassBuildItem.builder(CxfClientProducer.RUNTIME_INITIALIZED_PROXY_MARKER_INTERFACE_NAME)
.build());
copyMarkerInterfaceToApplication(generatedBeans);
}
}

/**
* Copies the {@value CxfClientProducer#RUNTIME_INITIALIZED_PROXY_MARKER_INTERFACE_NAME} from the current
* classloader
* to the user application. Why we have do that: First, the interface is package-visible so that adding it to
* the client proxy definition forces GraalVM to generate the proxy class in
* {@value CxfClientProducer#RUNTIME_INITIALIZED_PROXY_MARKER_INTERFACE_PACKAGE} package rather than under a random
* package/class name. Thanks to that we can request the postponed initialization of the generated proxy class by package
* name.
* More details in <a href="https://github.com/quarkiverse/quarkus-cxf/issues/580">#580</a>.
*
* @param generatedBeans
*/
private void copyMarkerInterfaceToApplication(BuildProducer<GeneratedBeanBuildItem> generatedBeans) {
byte[] bytes;
try {
bytes = IoUtil.readClassAsBytes(getClass().getClassLoader(),
CxfClientProducer.RUNTIME_INITIALIZED_PROXY_MARKER_INTERFACE_NAME);
} catch (IOException e) {
throw new RuntimeException("Could not read " + CxfClientProducer.RUNTIME_INITIALIZED_PROXY_MARKER_INTERFACE_NAME
+ ".class from quarkus-cxf-deployment jar");
}
String className = CxfClientProducer.RUNTIME_INITIALIZED_PROXY_MARKER_INTERFACE_NAME.replace('.', '/');
generatedBeans.produce(new GeneratedBeanBuildItem(className, bytes));
}

private void generateCxfClientProducer(
Expand Down Expand Up @@ -464,84 +398,4 @@ private void produceUnremovableBean(
.forEach(unremovables::produce);
}

private static class ProxyInfo {

public static ProxyInfo of(
boolean refersToRuntimeInitializedClasses,
ClassInfo wsClassInfo,
Set<String> rtInitClasses,
Set<String> rtInitPackages,
IndexView index) {
final List<String> result = new ArrayList<>();
result.add(wsClassInfo.name().toString());
result.add(BindingProvider.class.getName());
result.add("java.io.Closeable");
result.add(Client.class.getName());

if (!refersToRuntimeInitializedClasses) {
/* Try to auto-detect unless the user decided himself */
Predicate<String> isRuntimeInitializedClass = className -> rtInitClasses.contains(className)
|| rtInitPackages.contains(getPackage(className));
refersToRuntimeInitializedClasses = refersToRuntimeInitializedClasses(
wsClassInfo,
isRuntimeInitializedClass,
index);
}

if (refersToRuntimeInitializedClasses) {
result.add(io.quarkiverse.cxf.CxfClientProducer.RUNTIME_INITIALIZED_PROXY_MARKER_INTERFACE_NAME);
}
return new ProxyInfo(result, refersToRuntimeInitializedClasses);
}

static String getPackage(String className) {
int lastDot = className.lastIndexOf('.');
if (lastDot < 0) {
return "";
}
return className.substring(0, lastDot);
}

private static boolean refersToRuntimeInitializedClasses(ClassInfo wsClassInfo,
Predicate<String> isRuntimeInitializedClass, IndexView index) {
if (isRuntimeInitializedClass.test(wsClassInfo.name().toString())) {
return true;
}
boolean ownMethods = wsClassInfo.methods().stream()
.filter(m -> (m.flags() & java.lang.reflect.Modifier.STATIC) == 0) // only non-static methods
.anyMatch(m -> isRuntimeInitializedClass.test(m.returnType().name().toString())
|| m.parameterTypes().stream()
.map(Type::name)
.map(DotName::toString)
.anyMatch(isRuntimeInitializedClass));
if (ownMethods) {
return true;
}

/* Do the same recursively for all interfaces */
return wsClassInfo.interfaceNames().stream()
.map(intf -> {
final ClassInfo cl = index.getClassByName(intf);
if (cl == null) {
LOGGER.warnf(
"Could not check whether %s refers to runtime initialized classes because it was not found in Jandex",
intf);
}
return cl;
})
.filter(cl -> cl != null)
.anyMatch(cl -> refersToRuntimeInitializedClasses(cl, isRuntimeInitializedClass, index));
}

private ProxyInfo(List<String> interfaces, boolean isRuntimeInitialized) {
super();
this.interfaces = interfaces;
this.isRuntimeInitialized = isRuntimeInitialized;
}

private final List<String> interfaces;
private final boolean isRuntimeInitialized;

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ public class CXFClientData implements Serializable {
private String sei;
private String wsName;
private String wsNamespace;
private boolean proxyClassRuntimeInitialized;

public CXFClientData() {
}
Expand All @@ -26,13 +25,11 @@ public CXFClientData(
String soapBinding,
String sei,
String wsName,
String wsNamespace,
boolean proxyClassRuntimeInitialized) {
String wsNamespace) {
this.soapBinding = soapBinding;
this.sei = sei;
this.wsName = wsName;
this.wsNamespace = wsNamespace;
this.proxyClassRuntimeInitialized = proxyClassRuntimeInitialized;
}

public String getSoapBinding() {
Expand All @@ -51,14 +48,6 @@ public String getWsNamespace() {
return wsNamespace;
}

public boolean isProxyClassRuntimeInitialized() {
return proxyClassRuntimeInitialized;
}

public void setProxyClassRuntimeInitialized(boolean proxyClassRuntimeInitialized) {
this.proxyClassRuntimeInitialized = proxyClassRuntimeInitialized;
}

public void setWrapperClassNames(Set<String> classNames) {
this.wrapperClassNames = classNames;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ public class CXFClientInfo {
private String epName;
private String username;
private String password;
private boolean proxyClassRuntimeInitialized;
private final List<String> inInterceptors = new ArrayList<>();
private final List<String> outInterceptors = new ArrayList<>();
private final List<String> outFaultInterceptors = new ArrayList<>();
Expand All @@ -35,8 +34,7 @@ public CXFClientInfo(
String endpointAddress,
String soapBinding,
String wsNamespace,
String wsName,
boolean proxyClassRuntimeInitialized) {
String wsName) {
this.endpointAddress = endpointAddress;
this.epName = null;
this.epNamespace = null;
Expand All @@ -47,12 +45,10 @@ public CXFClientInfo(
this.wsName = wsName;
this.wsNamespace = wsNamespace;
this.wsdlUrl = null;
this.proxyClassRuntimeInitialized = proxyClassRuntimeInitialized;
}

public CXFClientInfo(CXFClientInfo other) {
this(other.sei, other.endpointAddress, other.soapBinding, other.wsNamespace, other.wsName,
other.proxyClassRuntimeInitialized);
this(other.sei, other.endpointAddress, other.soapBinding, other.wsNamespace, other.wsName);
this.wsdlUrl = other.wsdlUrl;
this.epNamespace = other.epNamespace;
this.epName = other.epName;
Expand Down Expand Up @@ -125,10 +121,6 @@ public String getPassword() {
return password;
}

public boolean isProxyClassRuntimeInitialized() {
return proxyClassRuntimeInitialized;
}

public List<String> getFeatures() {
return features;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ public RuntimeValue<CXFClientInfo> cxfClientInfoSupplier(CXFClientData cxfClient
format("%s/%s", DEFAULT_EP_ADDR, cxfClientData.getSei().toLowerCase()),
cxfClientData.getSoapBinding(),
cxfClientData.getWsNamespace(),
cxfClientData.getWsName(),
cxfClientData.isProxyClassRuntimeInitialized()));
cxfClientData.getWsName()));
}

private static class ServletConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,24 +66,7 @@ private Object produceCxfClient(CXFClientInfo cxfClientInfo) {
LOGGER.errorf("WebService interface (client) class %s not found", cxfClientInfo.getSei());
return null;
}
Class<?>[] interfaces;
try {
interfaces = cxfClientInfo.isProxyClassRuntimeInitialized()
? new Class<?>[] {
BindingProvider.class,
Closeable.class,
Client.class,
Class.forName(RUNTIME_INITIALIZED_PROXY_MARKER_INTERFACE_NAME, true,
Thread.currentThread().getContextClassLoader())
}
: new Class<?>[] {
BindingProvider.class,
Closeable.class,
Client.class
};
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not load " + RUNTIME_INITIALIZED_PROXY_MARKER_INTERFACE_NAME, e);
}
Class<?>[] interfaces = new Class<?>[] { BindingProvider.class, Closeable.class, Client.class };
QuarkusClientFactoryBean quarkusClientFactoryBean = new QuarkusClientFactoryBean();
QuarkusJaxWsProxyFactoryBean factory = new QuarkusJaxWsProxyFactoryBean(quarkusClientFactoryBean, interfaces);
factory.setServiceClass(seiClass);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ quarkus.cxf.client.clientWithRuntimeInitializedPayload.service-interface = io.qu
quarkus.cxf.client.clientWithRuntimeInitializedPayload.endpoint-namespace = http://www.jboss.org/eap/quickstarts/wscalculator/Calculator
quarkus.cxf.client.clientWithRuntimeInitializedPayload.endpoint-name = CalculatorService
quarkus.cxf.client.clientWithRuntimeInitializedPayload.native.runtime-initialized = true
quarkus.native.additional-build-args = --initialize-at-run-time=io.quarkiverse.cxf.client.it.rtinit.Operands\\,io.quarkiverse.cxf.client.it.rtinit.Result
quarkus.native.additional-build-args = -H:+UseNewExperimentalClassInitialization,--initialize-at-run-time=io.quarkiverse.cxf.client.it.rtinit.Operands\\,io.quarkiverse.cxf.client.it.rtinit.Result

quarkus.cxf.codegen.wsdl2java.includes = wsdl/*.wsdl
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,5 @@ quarkus.cxf.client.imageServiceClientWithWrappers.client-endpoint-url = http://l
quarkus.cxf.client.imageServiceClientWithWrappers.service-interface = io.quarkiverse.cxf.it.ws.mtom.awt.server.ImageServiceWithWrappers
quarkus.cxf.client.imageServiceClientWithWrappers.endpoint-namespace = "https://quarkiverse.github.io/quarkiverse-docs/quarkus-cxf/test/mtom-awt-with-wrappers"
quarkus.cxf.client.imageServiceClientWithWrappers.endpoint-name = ImageServiceWithWrappers

quarkus.native.additional-build-args = -H:+UseNewExperimentalClassInitialization