diff --git a/pom.xml b/pom.xml index 11e4132..a9b84f9 100755 --- a/pom.xml +++ b/pom.xml @@ -28,11 +28,11 @@ no.difi.oxalis oxalis - 4.1.1 + 4.1.2 oxalis-as4 - 4.1.7 + 4.1.8-SNAPSHOT jar diff --git a/src/main/java/no/difi/oxalis/as4/common/As4CommonModule.java b/src/main/java/no/difi/oxalis/as4/common/As4CommonModule.java index a8c7fe0..7161519 100644 --- a/src/main/java/no/difi/oxalis/as4/common/As4CommonModule.java +++ b/src/main/java/no/difi/oxalis/as4/common/As4CommonModule.java @@ -42,9 +42,7 @@ import org.apache.cxf.Bus; import org.apache.cxf.BusFactory; import org.apache.wss4j.dom.engine.WSSConfig; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import java.security.Provider; import java.security.Security; import static no.difi.oxalis.as4.common.AS4Constants.*; @@ -64,14 +62,8 @@ protected void configure() { new OxalisAlgorithmSuiteLoader(bus); BusFactory.setThreadDefaultBus(bus); + Security.setProperty("jdk.security.provider.preferred", "AES/GCM/NoPadding:BC"); WSSConfig.init(); - - // Make sure that BouncyCastle is the preferred security provider - final Provider[] providers = Security.getProviders(); - if (providers != null && providers.length > 0) - Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); - log.debug("Registering BouncyCastle as preferred Java security provider"); - Security.insertProviderAt(new BouncyCastleProvider(), 1); } @Provides diff --git a/src/main/java/no/difi/oxalis/as4/inbound/SetPolicyInInterceptor.java b/src/main/java/no/difi/oxalis/as4/inbound/SetPolicyInInterceptor.java index c5fad4a..62c8f89 100644 --- a/src/main/java/no/difi/oxalis/as4/inbound/SetPolicyInInterceptor.java +++ b/src/main/java/no/difi/oxalis/as4/inbound/SetPolicyInInterceptor.java @@ -6,7 +6,6 @@ import no.difi.oxalis.as4.util.PolicyService; import org.apache.cxf.phase.Phase; import org.apache.cxf.ws.policy.PolicyInInterceptor; -import org.apache.cxf.ws.policy.PolicyOutInterceptor; @Slf4j @Singleton diff --git a/src/main/java/no/difi/oxalis/as4/outbound/As4MessageSender.java b/src/main/java/no/difi/oxalis/as4/outbound/As4MessageSender.java index e481215..694a5b7 100644 --- a/src/main/java/no/difi/oxalis/as4/outbound/As4MessageSender.java +++ b/src/main/java/no/difi/oxalis/as4/outbound/As4MessageSender.java @@ -1,6 +1,7 @@ package no.difi.oxalis.as4.outbound; import com.google.inject.Inject; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import no.difi.oxalis.api.outbound.TransmissionRequest; import no.difi.oxalis.api.outbound.TransmissionResponse; @@ -37,6 +38,7 @@ import javax.xml.ws.Service; import javax.xml.ws.soap.SOAPBinding; import java.io.IOException; +import java.io.InputStream; import java.util.*; import static no.difi.oxalis.as4.common.AS4Constants.CEF_CONFORMANCE; @@ -75,8 +77,10 @@ public As4MessageSender(MessagingProvider messagingProvider, MessageIdGenerator } public TransmissionResponse send(TransmissionRequest request) throws OxalisAs4TransmissionException { + AttachmentHolder attachmentHolder = null; try (DispatchImpl dispatch = createDispatch(request)) { - Collection attachments = prepareAttachments(request); + attachmentHolder = prepareAttachment(request); + ArrayList attachments = new ArrayList<>(Collections.singletonList(attachmentHolder.attachment)); dispatch.getRequestContext().put(Message.ATTACHMENTS, attachments); Messaging messaging = messagingProvider.createMessagingHeader(request, attachments); @@ -85,6 +89,14 @@ public TransmissionResponse send(TransmissionRequest request) throws OxalisAs4Tr return invoke(request, dispatch); } catch (IOException e) { throw new OxalisAs4TransmissionException("Failed to send message", e); + } finally { + if (attachmentHolder != null) { + try { + attachmentHolder.inputStream.close(); + } catch (IOException e) { + log.error("Couldn't close attachment input stream", e); + } + } } } @@ -119,7 +131,7 @@ private void configureSecurity(TransmissionRequest request, Dispatch prepareAttachments(TransmissionRequest request) throws OxalisAs4TransmissionException { + public AttachmentHolder prepareAttachment(TransmissionRequest request) throws OxalisAs4TransmissionException { Map> headers = new HashMap<>(); headers.put("Content-ID", Collections.singletonList(getContentID(request))); @@ -127,8 +139,10 @@ public Collection prepareAttachments(TransmissionRequest request) th headers.put("MimeType", Collections.singletonList("application/xml")); try { - Attachment attachment = AttachmentUtil.createAttachment(compressionUtil.getCompressedStream(request.getPayload()), headers); - return new ArrayList<>(Collections.singletonList(attachment)); + InputStream compressedStream = compressionUtil.getCompressedStream(request.getPayload()); + Attachment attachment = AttachmentUtil.createAttachment(compressedStream, headers); + + return new AttachmentHolder(compressedStream, attachment); } catch (IOException e) { throw new OxalisAs4TransmissionException("Unable to compress payload", e); } @@ -179,4 +193,10 @@ private Service getService(TransmissionRequest request) throws OxalisAs4Transmis service.addPort(PORT_NAME, SOAPBinding.SOAP12HTTP_BINDING, request.getEndpoint().getAddress().toString()); return service; } + + @RequiredArgsConstructor + private static class AttachmentHolder { + private final InputStream inputStream; + private final Attachment attachment; + } } diff --git a/src/main/java/no/difi/oxalis/as4/util/CompressionUtil.java b/src/main/java/no/difi/oxalis/as4/util/CompressionUtil.java index 6a000ed..0f85a26 100644 --- a/src/main/java/no/difi/oxalis/as4/util/CompressionUtil.java +++ b/src/main/java/no/difi/oxalis/as4/util/CompressionUtil.java @@ -1,21 +1,14 @@ package no.difi.oxalis.as4.util; -import com.google.inject.Inject; -import org.apache.commons.io.IOUtils; +import org.apache.cxf.helpers.IOUtils; +import org.apache.cxf.io.CacheSizeExceededException; +import org.apache.cxf.io.CachedOutputStream; -import javax.inject.Named; -import java.io.*; -import java.util.concurrent.ExecutorService; +import java.io.IOException; +import java.io.InputStream; import java.util.zip.GZIPOutputStream; -// https://stackoverflow.com/a/24673254 public class CompressionUtil { - private final ExecutorService pool; - - @Inject - public CompressionUtil(@Named("compression-pool") ExecutorService executorService) { - this.pool = executorService; - } /** * Gets Compressed Stream for given input Stream @@ -30,27 +23,16 @@ public InputStream getCompressedStream(final InputStream sourceStream) throws IO throw new IllegalArgumentException("Source Stream cannot be NULL"); } - final PipedInputStream resultStream = new PipedInputStream(); - final PipedOutputStream intermediateStream = new PipedOutputStream(resultStream); - final OutputStream zipperOutStream = new GZIPOutputStream(intermediateStream); - - Runnable copyTask = () -> { - try { - IOUtils.copy(sourceStream, zipperOutStream); - zipperOutStream.flush(); - } catch (IOException e) { - IOUtils.closeQuietly(resultStream); // close it on error case only - throw new RuntimeException(e); - } finally { - // close source stream and intermediate streams - IOUtils.closeQuietly(sourceStream); - IOUtils.closeQuietly(zipperOutStream); - IOUtils.closeQuietly(intermediateStream); - } - }; + CachedOutputStream cache = new CachedOutputStream(); - pool.submit(copyTask); - - return resultStream; + try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(cache)) { + IOUtils.copyAndCloseInput(sourceStream, gzipOutputStream); + gzipOutputStream.flush(); + gzipOutputStream.finish(); + return cache.getInputStream(); + } catch (CacheSizeExceededException | IOException cee) { + sourceStream.close(); + throw cee; + } } } \ No newline at end of file diff --git a/src/main/java/no/difi/oxalis/as4/util/Constants.java b/src/main/java/no/difi/oxalis/as4/util/Constants.java index f37b839..e8182dd 100755 --- a/src/main/java/no/difi/oxalis/as4/util/Constants.java +++ b/src/main/java/no/difi/oxalis/as4/util/Constants.java @@ -1,18 +1,20 @@ package no.difi.oxalis.as4.util; +import lombok.experimental.UtilityClass; + import javax.xml.namespace.QName; -public interface Constants { +@UtilityClass +public class Constants { - String EBMS_NAMESPACE = "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/"; + public static final String EBMS_NAMESPACE = "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/"; - QName MESSAGING_QNAME = new QName(EBMS_NAMESPACE, "Messaging", "eb"); - QName USER_MESSAGE_QNAME = new QName(EBMS_NAMESPACE, "UserMessage"); - QName SIGNAL_MESSAGE_QNAME = new QName(EBMS_NAMESPACE, "SignalMessage"); + public static final QName MESSAGING_QNAME = new QName(EBMS_NAMESPACE, "Messaging", "eb"); + public static final QName USER_MESSAGE_QNAME = new QName(EBMS_NAMESPACE, "UserMessage"); + public static final QName SIGNAL_MESSAGE_QNAME = new QName(EBMS_NAMESPACE, "SignalMessage"); - String RSA_SHA256 = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; - String DIGEST_ALGORITHM_SHA256 = "sha256"; + public static final String DIGEST_ALGORITHM_SHA256 = "sha256"; - String TEST_SERVICE = "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/service"; - String TEST_ACTION = "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/test"; + public static final String TEST_SERVICE = "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/service"; + public static final String TEST_ACTION = "http://docs.oasis-open.org/ebxml-msg/ebms/v3.0/ns/core/200704/test"; } diff --git a/src/main/java/no/difi/oxalis/as4/util/GeneralUtils.java b/src/main/java/no/difi/oxalis/as4/util/GeneralUtils.java index f99a18c..1d3a36f 100644 --- a/src/main/java/no/difi/oxalis/as4/util/GeneralUtils.java +++ b/src/main/java/no/difi/oxalis/as4/util/GeneralUtils.java @@ -1,13 +1,16 @@ package no.difi.oxalis.as4.util; +import lombok.experimental.UtilityClass; + import java.util.Iterator; import java.util.Spliterators; import java.util.stream.Stream; import java.util.stream.StreamSupport; +@UtilityClass public class GeneralUtils { - public static Stream iteratorToStreamOfUnknownSize(Iterator iterator, int characteristics, boolean parallel){ + public static Stream iteratorToStreamOfUnknownSize(Iterator iterator, int characteristics, boolean parallel) { return StreamSupport.stream( Spliterators.spliteratorUnknownSize(iterator, characteristics), parallel); diff --git a/src/main/java/no/difi/oxalis/as4/util/PeppolConfiguration.java b/src/main/java/no/difi/oxalis/as4/util/PeppolConfiguration.java index 629448f..f4bc793 100644 --- a/src/main/java/no/difi/oxalis/as4/util/PeppolConfiguration.java +++ b/src/main/java/no/difi/oxalis/as4/util/PeppolConfiguration.java @@ -2,16 +2,18 @@ import lombok.Getter; import no.difi.oxalis.api.tag.Tag; -import org.apache.wss4j.dom.handler.WSHandlerConstants; import java.util.Arrays; import java.util.List; +import static org.apache.wss4j.dom.handler.WSHandlerConstants.ENCRYPT; +import static org.apache.wss4j.dom.handler.WSHandlerConstants.SIGNATURE; + @Getter public class PeppolConfiguration implements Tag { public static final String IDENTIFIER = "AS4.OUTBOUND.PEPPOL"; - private List actions = Arrays.asList(WSHandlerConstants.SIGNATURE, WSHandlerConstants.ENCRYPT); + private List actions = Arrays.asList(SIGNATURE, ENCRYPT); private String partyIDType = "urn:fdc:peppol.eu:2017:identifiers:ap"; private String agreementRef = "urn:fdc:peppol.eu:2017:agreements:tia:ap_provider"; diff --git a/src/main/java/no/difi/oxalis/as4/util/SOAPHeaderParser.java b/src/main/java/no/difi/oxalis/as4/util/SOAPHeaderParser.java index ceffdc8..22569a9 100644 --- a/src/main/java/no/difi/oxalis/as4/util/SOAPHeaderParser.java +++ b/src/main/java/no/difi/oxalis/as4/util/SOAPHeaderParser.java @@ -1,6 +1,7 @@ package no.difi.oxalis.as4.util; import com.google.common.collect.Lists; +import lombok.experimental.UtilityClass; import no.difi.oxalis.as4.lang.OxalisAs4Exception; import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.Messaging; import org.oasis_open.docs.ebxml_msg.ebms.v3_0.ns.core._200704.UserMessage; @@ -22,6 +23,7 @@ import java.util.Collections; import java.util.List; +@UtilityClass public class SOAPHeaderParser { private static final String NS_ALL = "*"; diff --git a/src/main/java/no/difi/oxalis/as4/util/XMLUtil.java b/src/main/java/no/difi/oxalis/as4/util/XMLUtil.java index bddcfc0..9997a18 100644 --- a/src/main/java/no/difi/oxalis/as4/util/XMLUtil.java +++ b/src/main/java/no/difi/oxalis/as4/util/XMLUtil.java @@ -1,5 +1,6 @@ package no.difi.oxalis.as4.util; +import lombok.experimental.UtilityClass; import no.difi.oxalis.as4.lang.OxalisAs4Exception; import javax.xml.datatype.DatatypeConfigurationException; @@ -8,15 +9,16 @@ import java.util.Date; import java.util.GregorianCalendar; +@UtilityClass public class XMLUtil { - public static XMLGregorianCalendar dateToXMLGeorgianCalendar(Date date) throws OxalisAs4Exception{ + public static XMLGregorianCalendar dateToXMLGeorgianCalendar(Date date) throws OxalisAs4Exception { try { GregorianCalendar gc = new GregorianCalendar(); gc.setTime(date); return DatatypeFactory.newInstance().newXMLGregorianCalendar(gc); - }catch (DatatypeConfigurationException e){ + } catch (DatatypeConfigurationException e) { throw new OxalisAs4Exception("Unable to convert timestamp to XML", e); } } diff --git a/src/test/java/no/difi/oxalis/as4/util/CompressionUtilTest.java b/src/test/java/no/difi/oxalis/as4/util/CompressionUtilTest.java index 7e9659f..d1b2c60 100644 --- a/src/test/java/no/difi/oxalis/as4/util/CompressionUtilTest.java +++ b/src/test/java/no/difi/oxalis/as4/util/CompressionUtilTest.java @@ -6,26 +6,30 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; -import java.nio.charset.Charset; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; +import java.util.Random; import java.util.zip.GZIPInputStream; public class CompressionUtilTest { - static final String DATA = "Lorem ipsum dolor sit amet"; - @Test - public void simple() throws Exception{ - InputStream sourceStream = new ByteArrayInputStream(DATA.getBytes()); - ExecutorService executor = Executors.newSingleThreadExecutor(); - - InputStream compressedStream = new CompressionUtil(executor).getCompressedStream(sourceStream); - - GZIPInputStream decompressedStream = new GZIPInputStream(compressedStream); - List lines = IOUtils.readLines(decompressedStream, Charset.defaultCharset()); + public void simple() throws Exception { + byte[] before = "Lorem ipsum dolor sit amet".getBytes(); + InputStream sourceStream = new ByteArrayInputStream(before); + InputStream compressedStream = new CompressionUtil().getCompressedStream(sourceStream); + try (GZIPInputStream decompressedStream = new GZIPInputStream(compressedStream)) { + byte[] after = IOUtils.toByteArray(decompressedStream); + Assert.assertEquals(before, after); + } + } - Assert.assertEquals(1, lines.size()); - Assert.assertEquals(DATA, lines.get(0)); + @Test + public void cachedInTempFile() throws Exception { + byte[] before = new byte[1024 * 1024]; + new Random().nextBytes(before); + InputStream sourceStream = new ByteArrayInputStream(before); + InputStream compressedStream = new CompressionUtil().getCompressedStream(sourceStream); + try (GZIPInputStream decompressedStream = new GZIPInputStream(compressedStream)) { + byte[] after = IOUtils.toByteArray(decompressedStream); + Assert.assertEquals(before, after); + } } }