java - JAX-WS 序列化为流

标签 java soap cxf jax-ws axis

在我的项目中,我们正在实现一项新功能,其中涉及与 Web 服务对话。我想使用 JAX-WS 来做到这一点,因为它似乎是最简单的,也是所有教程的做法。

现在我们的应用程序中已经有一些 Web 服务调用,但这些调用使用 Axis 1.4,并且因为 Axis 提供了一些类(Service、QName)自己的实现,所以当我尝试与新 Web 通信时,我遇到了一些类加载器问题使用 JAX-WS 提供服务。

我考虑过用 jre 内置的 JAX-WS RI 替换 Axis,但我们使用 Axis 将请求序列化为字节数组,然后以某种方式将其发送到其他系统。

ByteArrayOutputStream outStream = new ByteArrayOutputStream();

// provide an AXIS configuration which overwrites the default HTTP
// handler with our handler
// implementation which writes the SOAP message into the given output
// stream.
SimpleProvider config = new SimpleProvider();
config.deployTransport("http", new SimpleTargetedChain(new ByteArraySender(outStream)));

ArchiveService service = new ArchiveServiceLocator(config);
Archive archive = service.getArchive()
archive.archiveReportWithDefTags(metaData, dataHandler);
return outStream.toString(HTTPConstants.HEADER_DEFAULT_CHAR_ENCODING);

是否可以使用 JAX-WS RI 或 CXF 执行类似的操作(将请求序列化到本地变量/流)?我更喜欢使用这两个而不是 Axis2,因为我在 SO 上读过几次,认为它们是首选。

最佳答案

对于遇到同样问题的人,我找到了 CXF 和 Metro (JAX-WS RI) 的解决方案,但它们最终都没有出现在我的应用程序中,因为它们将我带入类路径 hell 。相反,我选择了旧的 Axis (1.x),因为它可以正常工作。

CXF

对于 CXF,我需要在总线中注册一个自定义 ConduitInitiator。 ConduitInitiator 会将请求保存到自定义 Conduit 中的 OutputStream 中。

配置/设置类:

import javax.xml.ws.soap.MTOMFeature;

import org.apache.cxf.Bus;
import org.apache.cxf.BusFactory;
import org.apache.cxf.transport.Conduit;
import org.apache.cxf.transport.ConduitInitiatorManager;

public class ArchiveMessageSerializer {

    public String serializeArchiveReportWithDefTags(ArchiveMetadataType metaData) throws Exception {
        final SavingConduit savingConduit = new SavingConduit();
        registerCustomTransport(savingConduit);
        try {
            final Service service = new Service();
            final Port port = service.getArchive(new MTOMFeature(true));
            port.someMethod(metaData);
        }
        finally {
            tearDownBusWithCustomTransport();
        }
        return savingConduit.getResult().toString("iso-8859-1");
    }

    private void registerCustomTransport(Conduit conduit) {
        Bus bus = BusFactory.getThreadDefaultBus();

        CustomConduitInitiator customTransport = new CustomConduitInitiator(conduit);

        ConduitInitiatorManager cim = bus.getExtension(ConduitInitiatorManager.class);

        cim.registerConduitInitiator("http://schemas.xmlsoap.org/soap/http", customTransport);
        cim.registerConduitInitiator("http://schemas.xmlsoap.org/wsdl/soap/http", customTransport);
    }

    private void tearDownBusWithCustomTransport() {
        BusFactory.setThreadDefaultBus(null);
    }
}

管道启动器:

import org.apache.cxf.Bus;
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.transport.Conduit;
import org.apache.cxf.transport.ConduitInitiator;
import org.apache.cxf.ws.addressing.EndpointReferenceType;

class CustomConduitInitiator implements ConduitInitiator {

    private final Conduit fMyConduit;

    public CustomConduitInitiator(Conduit conduit) {
        fMyConduit = conduit;
    }

    @Override
    public Conduit getConduit(EndpointInfo targetInfo, Bus bus) throws IOException {
        return fMyConduit;
    }

    @Override
    public Conduit getConduit(EndpointInfo localInfo, EndpointReferenceType target, Bus bus) throws IOException {
        return fMyConduit;
    }

    @Override
    public Set<String> getUriPrefixes() {
        return null;
    }

    @Override
    public List<String> getTransportIds() {
        return null;
    }
}

导管:

import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageImpl;
import org.apache.cxf.transport.Conduit;
import org.apache.cxf.transport.MessageObserver;
import org.apache.cxf.ws.addressing.EndpointReferenceType;

class SavingConduit implements Conduit {

    private final ByteArrayOutputStream fOutputStream;

    private Exchange fExchange;
    private MessageObserver fObserver;

    SavingConduit() {
        fOutputStream = new ByteArrayOutputStream();
    }

    ByteArrayOutputStream getResult() {
        return fOutputStream;
    }

    @Override
    public void prepare(Message message) throws IOException {
        fExchange = message.getExchange();
        message.setContent(OutputStream.class, fOutputStream);
    }

    @Override
    public void close(Message message) throws IOException {
        final OutputStream outputStream = message.getContent(OutputStream.class);
        if (outputStream != null) {
            outputStream.close();

            message.put(Message.RESPONSE_CODE, HttpURLConnection.HTTP_OK);

            final Message inMessage = new MessageImpl();
            inMessage.put(Message.HTTP_REQUEST_METHOD, "GET");
            inMessage.setContent(InputStream.class, new ByteArrayInputStream(new byte[0]));
            inMessage.setExchange(fExchange);
            fExchange.setInMessage(inMessage);
            fObserver.onMessage(inMessage);
        }

        final InputStream inputStream = message.getContent(InputStream.class);
        if (inputStream != null) {
            inputStream.close();
        }
    }

    @Override
    public EndpointReferenceType getTarget() {
        return null;
    }

    @Override
    public void close() {
    }

    @Override
    public void setMessageObserver(MessageObserver observer) {
        fObserver = observer;
    }

    @Override
    public MessageObserver getMessageObserver() {
        return fObserver;
    }
}

地铁

对于 Metro,我必须将 TransportTubeFactory 注册为服务。 Metro 会选择我的类并将其用作创建我的 Tube 的候选工厂,这是 Metro 的客户端传输机制的概念。

文件 META-INF/services/com.sun.xml.ws.api.pipe.TransportTubeFactory:

com.mypackage.RedirectionTransportFactory

设置代码:

import javax.xml.ws.soap.MTOMFeature;

public class ArchiveMessageSerializer {

    public String serializeArchiveReportWithDefTags(ArchiveMetadataType metaData) throws UnsupportedEncodingException {
        final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        RedirectionTransportFactory.getRegistry().registerRedirectionInThread(buffer);
        final Service service = new Service();
        final Port port = service.getPort(new MTOMFeature());
        port.someMethod(metaData);
        return buffer.toString("iso-8859-1");
    }
}

运输工厂:

import com.sun.xml.ws.api.pipe.ClientTubeAssemblerContext;
import com.sun.xml.ws.api.pipe.TransportTubeFactory;
import com.sun.xml.ws.api.pipe.Tube;

public class RedirectionTransportFactory extends TransportTubeFactory {
    private static final RedirectionTransportRegistry REGISTRY = new RedirectionTransportRegistry();

    public static RedirectionTransportRegistry getRegistry() {
        return REGISTRY;
    }

    @Override
    public Tube doCreate(ClientTubeAssemblerContext context) {
        return getRegistry().createTubeFor(context);
    }
}

我的输出流注册表:

import com.sun.xml.ws.api.pipe.ClientTubeAssemblerContext;
import com.sun.xml.ws.api.pipe.Tube;

public class RedirectionTransportRegistry {
    private final ThreadLocal<RegisteredTube> fCurrentThreadTube = new ThreadLocal<RegisteredTube>();

    Tube createTubeFor(ClientTubeAssemblerContext context) {
        final RegisteredTube registeredTube = fCurrentThreadTube.get();
        if (registeredTube == null) {
            return null;
        }
        return registeredTube.createTube(context);
    }

    public void registerRedirectionInThread(OutputStream outputStream) {
        final RegisteredTube registeredTube = new RegisteredTube(outputStream);
        fCurrentThreadTube.set(registeredTube);
    }

    private class RegisteredTube {

        private final OutputStream fOutputStream;

        public RegisteredTube(OutputStream outputStream) {
            fOutputStream = outputStream;
        }

        Tube createTube(ClientTubeAssemblerContext context) {
            return new RedirectionTube(context.getCodec(), fOutputStream);
        }
    }
}

将消息写入给定输出流的管:

import javax.xml.ws.WebServiceException;

import com.sun.xml.ws.api.message.Packet;
import com.sun.xml.ws.api.pipe.Codec;
import com.sun.xml.ws.api.pipe.NextAction;
import com.sun.xml.ws.api.pipe.TubeCloner;
import com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl;

public class RedirectionTube extends AbstractTubeImpl {

    private final Codec fCodec;
    private final OutputStream fOutputStream;

    public RedirectionTube(Codec codec, OutputStream outputStream) {
        fCodec = codec;
        fOutputStream = outputStream;
    }

    @Override
    public AbstractTubeImpl copy(TubeCloner cloner) {
        return this;
    }

    @Override
    public NextAction processRequest(Packet request) {
        return doReturnWith(process(request));
    }

    @Override
    public NextAction processResponse(Packet response) {
        throw new IllegalStateException("MyAbstractTubeImpl's processException shouldn't be called.");
    }

    @Override
    public NextAction processException(Throwable t) {
        throw new IllegalStateException("MyAbstractTubeImpl's processException shouldn't be called.");
    }

    @Override
    public void preDestroy() {
    }

    @Override
    public Packet process(Packet p) {
        try {
            fCodec.encode(p, fOutputStream);

            return p.createClientResponse(null);
        }
        catch (IOException e) {
            throw new WebServiceException(e);
        }
    }
}

结论

尽管 CXF 有更好的文档,但一旦我了解了 META-INF 文件以及要实现的类(TransportTubeFactory),实际实现 Metro 版本就更容易了。 CXF 有一个更复杂的管道,其中的拦截器都处理相同的消息,而 Metro 使用更干净的网络堆栈式架构及其管道。

我认为尽管他们能够做这种事情,但他们中没有一个人真正适合真正做这件事。由于它们隐藏在 JAX-WS API 后面,因此很难配置。根据您的 JRE 附带的实现,您将陷入类路径 hell 。

关于java - JAX-WS 序列化为流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27171620/

相关文章:

java - RemoveChild 删除具有该名称的第一个子项,但跳过下一个具有相同名称的子项

php - 在 php 中发送 SOAP 身份验证 header 的问题

java - Apache CXF 附件安全

java - 带字符串的 CXF header 元素

java - 吉德CheckBoxListComboBox中的确定和取消按钮句柄

java - 通过 iCalendar vEvent 禁用 outlook "propose new time"按钮

java - 更改 .dbf 文件的字符集

java - 如何从java中的SOAP端点获取响应?

php - 在 SOAP 请求上收到 400 个错误请求(传输编码 : chunked problem?)

java - jax-rs 响应实体类