Java tutorial
/************************************************************************* * (c) Copyright 2016 Hewlett Packard Enterprise Development Company LP * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses/. ************************************************************************/ package com.eucalyptus.ws.handlers; import java.util.Iterator; import java.util.List; import java.util.function.Supplier; import javax.annotation.Nullable; import javax.xml.soap.SOAPConstants; import org.apache.axiom.om.OMElement; import org.apache.axiom.soap.SOAPEnvelope; import org.apache.axiom.soap.SOAPFactory; import org.apache.axiom.soap.SOAPFault; import org.apache.axiom.soap.SOAPHeader; import org.apache.axiom.soap.SOAPHeaderBlock; import com.eucalyptus.binding.Binding; import com.eucalyptus.binding.HoldMe; import com.eucalyptus.records.Logs; import com.eucalyptus.util.Exceptions; import com.eucalyptus.util.Pair; import com.eucalyptus.ws.EucalyptusRemoteFault; import com.eucalyptus.ws.IoMessage; import com.google.common.collect.Lists; import edu.ucsb.eucalyptus.msgs.EucalyptusErrorMessageType; import edu.ucsb.eucalyptus.msgs.ExceptionResponseType; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.handler.codec.http.HttpMessage; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; import javaslang.control.Option; /** * SOAP object model handler */ @SuppressWarnings("ThrowableResultOfMethodCallIgnored") @ChannelHandler.Sharable public class IoSoapHandler extends ChannelDuplexHandler { /** * Handle incoming soap model */ @Override public void channelRead(final ChannelHandlerContext ctx, final Object msg) throws Exception { if (msg instanceof IoMessage) { final IoMessage ioMessage = IoMessage.class.cast(msg); final SOAPEnvelope env = ioMessage.getSoapEnvelope(); if (env != null && !env.hasFault()) { ioMessage.setOmMessage(env.getBody().getFirstElement()); } else { final Supplier<Integer> statusCodeSupplier = () -> getStatus(ioMessage); perhapsFault(env, statusCodeSupplier); } } super.channelRead(ctx, msg); } /** * Create outbound soap model */ @Override public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) throws Exception { if (msg instanceof IoMessage) { final IoMessage ioMessage = IoMessage.class.cast(msg); final HttpMessage httpMessage = ioMessage.getHttpMessage(); final Option<Pair<SOAPEnvelope, Integer>> soapEnvelopeOption = IoSoapHandler .perhapsBuildFault(ioMessage.getMessage()); if (soapEnvelopeOption.isDefined()) { ioMessage.setSoapEnvelope(soapEnvelopeOption.get().getLeft()); if (httpMessage instanceof HttpResponse) { ((HttpResponse) httpMessage) .setStatus(HttpResponseStatus.valueOf(soapEnvelopeOption.get().getRight())); } } else { ioMessage.setSoapEnvelope(buildSoapEnvelope(ioMessage.getOmMessage())); } } super.write(ctx, msg, promise); } @Nullable private static Integer getStatus(final IoMessage message) { return message.getHttpMessage() instanceof HttpResponse ? ((HttpResponse) message.getHttpMessage()).getStatus().code() : null; } static SOAPEnvelope buildSoapEnvelope(final OMElement body) { final SOAPFactory factory = HoldMe.getOMSOAP11Factory(); final SOAPEnvelope soapEnvelope = factory .createSOAPEnvelope(factory.createOMNamespace(SOAPConstants.URI_NS_SOAP_1_1_ENVELOPE, "")); factory.createSOAPHeader(soapEnvelope); factory.createSOAPBody(soapEnvelope).addChild(body); return soapEnvelope; } static Option<Pair<SOAPEnvelope, Integer>> perhapsBuildFault(final Object msg) { Option<Pair<SOAPEnvelope, Integer>> soapEnvelopeOption = Option.none(); if (msg instanceof EucalyptusErrorMessageType) { EucalyptusErrorMessageType errMsg = (EucalyptusErrorMessageType) msg; soapEnvelopeOption = Option.some( Pair.of(Binding.createFault(errMsg.getSource(), errMsg.getMessage(), errMsg.getStatusMessage()), org.jboss.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST.getCode())); } else if (msg instanceof ExceptionResponseType) { ExceptionResponseType errMsg = (ExceptionResponseType) msg; String createFaultDetails = Logs.isExtrrreeeme() ? Exceptions.string(errMsg.getException()) : errMsg.getException().getMessage(); soapEnvelopeOption = Option.some( Pair.of(Binding.createFault(errMsg.getRequestType(), errMsg.getMessage(), createFaultDetails), errMsg.getHttpStatus().getCode())); } return soapEnvelopeOption; } static void perhapsFault(final SOAPEnvelope env, final Supplier<Integer> statusCodeSupplier) throws EucalyptusRemoteFault { final SOAPHeader header = env.getHeader(); String action = "ProblemAction"; String relatesTo = "RelatesTo"; if (header != null) { final List<SOAPHeaderBlock> headers = Lists.newArrayList(header.examineAllHeaderBlocks()); // :: try to get the fault info from the soap header -- hello there? ::// for (final SOAPHeaderBlock headerBlock : headers) { if (action.equals(headerBlock.getLocalName())) { action = headerBlock.getText(); } else if (relatesTo.equals(headerBlock.getLocalName())) { relatesTo = headerBlock.getText(); } } } //faults don't need to have a header. // :: process the real fault ::// final SOAPFault fault = env.getBody().getFault(); if (fault != null) { String faultReason = ""; final Iterator children = fault.getChildElements(); while (children.hasNext()) { final OMElement child = (OMElement) children.next(); faultReason += child.getText(); } final String faultCode = fault.getCode().getText(); faultReason = faultReason.replaceAll(faultCode, ""); final String faultDetail = fault.getDetail().getText(); throw new EucalyptusRemoteFault(action, relatesTo, faultCode, faultReason, faultDetail, statusCodeSupplier.get()); } } }