/**
*
* 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.openejb.server.cxf.client;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.openejb.OpenEJBException;
import org.apache.openejb.assembler.classic.OpenEjbConfiguration;
import org.apache.openejb.assembler.classic.ServiceInfo;
import org.apache.openejb.assembler.classic.util.ServiceInfos;
import org.apache.openejb.config.ConfigurationFactory;
import org.apache.openejb.config.sys.Openejb;
import org.apache.openejb.config.sys.Service;
import org.apache.openejb.core.ivm.naming.JaxWsServiceReference;
import org.apache.openejb.loader.SystemInstance;
import org.apache.openejb.util.LogCategory;
import org.apache.openejb.util.Logger;
import javax.xml.namespace.QName;
import javax.xml.ws.WebServiceFeature;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import static java.util.Arrays.asList;
import static org.apache.openejb.server.cxf.transport.util.CxfUtil.configureInterceptors;
/**
* Used to configure a @WebServiceRef.
* Config uses application.properties.
* Format is:
*
* # global for all clients
* cxf.jaxws.client.out-interceptors = com.foo.MyInInterceptor
* # specific
* cxf.jaxws.client.{ns}MyPort.out-interceptors = com.foo.MyInInterceptor
*
* Services can be defines in tomee.xml or application.properties.
*
* Note: resources.xml are ignored for now (to be enhanced)
*/
public class WebServiceInjectionConfigurator implements JaxWsServiceReference.WebServiceClientCustomizer {
private static final String CXF_JAXWS_CLIENT_PREFIX = "cxf.jaxws.client.";
@Override
public WebServiceFeature[] features(final QName qname, final Properties properties) {
Collection<WebServiceFeature> list = null;
for (final String suffix : asList("", (qname == null ? "_" : qname.toString()) + ".")) {
final String wsFeatures = properties.getProperty(CXF_JAXWS_CLIENT_PREFIX + suffix + "wsFeatures");
if (wsFeatures != null) {
final Collection<Object> instances = ServiceInfos.resolve(createServiceInfos(properties), wsFeatures.split(" *, *"));
if (instances != null && !instances.isEmpty()) {
for (final Object i : instances) {
if (!WebServiceFeature.class.isInstance(i)) {
throw new IllegalArgumentException("Not a WebServiceFeature: " + i);
}
if (list == null) { // lazy to avoid useless allocation in most of cases
list = new LinkedList<>();
}
list.add(WebServiceFeature.class.cast(i));
}
}
}
}
return list != null ? list.toArray(new WebServiceFeature[list.size()]) : null;
}
@Override
public void customize(final Object o, final Properties properties) {
final Client client;
try {
client = ClientProxy.getClient(o);
} catch (final Exception e) {
return;
}
configure(client, properties);
}
private void configure(final Client client, final Properties properties) {
if (properties == null) {
return;
}
for (final String suffix : asList("", client.getEndpoint().getEndpointInfo().getName().toString() + ".")) {
// here (ie at runtime) we have no idea which services were linked to the app
// so using tomee.xml ones for now (not that shocking since we externalize the config with this class)
configureInterceptors(client, CXF_JAXWS_CLIENT_PREFIX + suffix, lazyServiceInfoList(properties), properties);
}
}
private List<ServiceInfo> lazyServiceInfoList(final Properties properties) { // don't create service info if not needed, ie no conf
return List.class.cast(
Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
new Class<?>[]{List.class},
new InvocationHandler() {
private List<ServiceInfo> list = null;
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
if (list == null) {
list = createServiceInfos(properties);
}
try {
return method.invoke(list, args);
} catch (final InvocationTargetException ite) {
throw ite.getCause();
}
}
}
)
);
}
private List<ServiceInfo> createServiceInfos(final Properties properties) {
final OpenEjbConfiguration config = SystemInstance.get().getComponent(OpenEjbConfiguration.class);
final List<ServiceInfo> services = new ArrayList<>(config.facilities != null && config.facilities.services != null ? config.facilities.services : Collections.<ServiceInfo>emptyList());
services.addAll(getServices(properties));
return services;
}
private Collection<ServiceInfo> getServices(final Properties properties) {
final ConfigurationFactory cf = SystemInstance.get().getComponent(ConfigurationFactory.class);
if (cf == null || !ConfigurationFactory.class.isInstance(cf)) {
return Collections.emptyList();
}
final Openejb openejb = new Openejb();
ConfigurationFactory.fillOpenEjb(openejb, properties);
final List<Service> services = openejb.getServices();
if (services.isEmpty()) {
return Collections.emptyList();
}
final Collection<ServiceInfo> info = new ArrayList<>(services.size());
for (final Service s : services) {
final String prefix = s.getId() + ".";
for (final String key : properties.stringPropertyNames()) {
if (key.startsWith(prefix)) {
s.getProperties().put(key.substring(prefix.length()), properties.getProperty(key));
}
}
try {
info.add(cf.configureService(s, ServiceInfo.class));
} catch (final OpenEJBException e) {
throw new IllegalArgumentException(e);
}
}
return info;
}
}