AS4 Peppol
Purpose¶
Peppol AS4 Messaging Support
This DSL is based on the open source library Phase4: https://github.com/phax/phase4
See the folder https://github.com/phax/phase4/tree/master/phase4-peppol-client/src/test/java/com/helger/phase4/peppol for different examples on how to send messages via the Peppol AS4 client.
Methods¶
Binding name: p6.as4peppol
Method: AutoCloseable buildContextConfiguration(Map<String, String> mp)
Phase4 is designed to gather configuration from properties files provided at runtime. This does not suite the platform6 execution architecture hence the introduction of the context configuration
Context configuration replaces the use of properties files with a Map[:] of configuration attributes defined at runtime.
It is therefore essential that all calls to the DSL are made within the configuration context and the context is closed at the end of execution.
This method returns an AutoClosable
making the lifecycle of the context easy to control in Groovy:
p6.as4peppol.buildContextConfiguration( configMap ).withCloseable {
// Context aware DSL calls here...
}
To reduce the configuration required to build a context each time, many of the Phase4 required properties have been defaults so only overrides are required. The defaults are:
Key | Default Value |
---|---|
phase4.wss4j.syncsecurity | “true” |
global.debug | “false” |
global.production | “true” |
global.nostartupinfo | “true” |
global.datapath | P6_DATA + “/resources/as4” |
phase4.dump.path | P6_DATA + “/resources/as4/dump” |
org.apache.wss4j.crypto.provider | “org.apache.wss4j.common.crypto.Merlin” |
org.apache.wss4j.crypto.merlin.keystore.type | “pkcs12” |
org.apache.wss4j.crypto.merlin.keystore.file | P6_DATA + “/resources/as4/as4peppol-ap.p12” |
org.apache.wss4j.crypto.merlin.keystore.password | “peppol” |
org.apache.wss4j.crypto.merlin.keystore.provider | “BC” |
org.apache.wss4j.crypto.merlin.keystore.alias | “cert” |
org.apache.wss4j.crypto.merlin.load.cacerts | “false” |
org.apache.wss4j.crypto.merlin.truststore.type | “jks” |
org.apache.wss4j.crypto.merlin.truststore.file | P6_DATA + “/resources/as4/as4peppol-complete-truststore.jks” |
org.apache.wss4j.crypto.merlin.truststore.password | “peppol” |
org.apache.wss4j.crypto.merlin.keystore.private.password” | same as org.apache.wss4j.crypto.merlin.keystore.password |
Method: HttpClientSettings buildHttpSettings(SSLContext ctx)
Create a Phase4 HttpClientSettings
object from a given SSLContext. This allows the p6.securesocket DSL to be used to define the HttpClient attributes when POSTing AS4
Method: Phase4PeppolSender.Builder sendBuilder()
Create a new Builder for AS4 messages if the payload is present and the SBDH (openPEPPOL Business Message Envelope) should be created internally
Method: Phase4PeppolSender.SBDHBuilder sendSBDHBuilder()
Create a new Builder for AS4 messages if the SBDH payload is already present
Method: PeppolIdentifierFactory idFactory()
Simple wrapper over Phase4PeppolSender.IF
to allow id generation
Method: Class<Phase4PeppolSender> sender = Phase4PeppolSender.class
Simple wrapper to reference the Phase4PeppolSender class
Method: IAS4ClientBuildMessageCallback simpleMessageSendLogger()
A simple implementation of IAS4ClientBuildMessageCallback to log message details during the send process
Method: Tuple3<Integer, byte[], Map<String, String>> receive(final Map<String, String> headers, final byte[] ba, final Closure<Void>... messageReceipt)
Receive an openPEPPOL AS4 Message. A byte array containing the message content and a separate list of MIME headers are supplied as a Map as parameters.
This method takes an optional callback method to use during the receipt processing:
- Closure - messageReceipt: is called when an inbound openPEPPOL Message is identified. The parameters passed are:
filePath
Path of received message,messageId
: Unique id of the received message
The method returns a Tuple3 containing the following:
- Integer: the HTTP status code to response with (200 is good)
- String: the response entity as a byte array (typically a receipt)
- Map: map of headers to use in a response to this request
Method: void setPerformSBDHValueChecking(boolean performSBDHValueChecking)
Enable value checks when reading Peppol SBDH documents (inbound message validation) default: false
Method: void setPerformReceiverChecking(boolean performReceiverChecking)
Enable receiver consistency checks (inbound validation) default: false
Examples¶
HTTP openPEPPOL AS4 Send with Receipt checker¶
The following is an example of sending a UBL Invoice document (XML defined via script resource) to the Austrian Governments test endpoint:
import groovy.xml.DOMBuilder
import com.helger.peppol.sml.ESML
import com.helger.smpclient.peppol.SMPClientReadOnly
import com.helger.phase4.dump.AS4RawResponseConsumerWriteToFile
def xml = p6.resource.get( "UBLInvoice" )
def payloadElement = DOMBuilder.parse(new StringReader(xml)).getDocumentElement()
// Austrian Government id
def receiverID = p6.as4peppol.idFactory().createParticipantIdentifierWithDefaultScheme ( "9915:test" )
def configMap = [:]
configMap["global.debug"] = "true"
configMap["global.production"] = "false"
configMap["org.apache.wss4j.crypto.merlin.keystore.password"] = "password-in-here"
p6.as4peppol.buildContextConfiguration( configMap ).withCloseable {
def ctx = p6.securesocket.contextBuilder()
.setType( SecureContext.BundleType.ONE_WAY_TRUST_ANY )
.setProtocol( "TLSv1.2" )
.build();
def httpSettings = p6.as4peppol.buildHttpSettings( p6.securesocket.contextBuild( ctx ) )
def result = p6.as4peppol.sendBuilder()
.documentTypeID ( p6.as4peppol.idFactory().createDocumentTypeIdentifierWithDefaultScheme ("urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##urn:cen.eu:en16931:2017#compliant#urn:fdc:peppol.eu:2017:poacc:billing:3.0::2.1") )
.processID ( p6.as4peppol.idFactory().createProcessIdentifierWithDefaultScheme ("urn:fdc:peppol.eu:2017:poacc:billing:01:1.0") )
.senderParticipantID ( p6.as4peppol.idFactory().createParticipantIdentifierWithDefaultScheme ("9915:phase4-test-sender") )
.receiverParticipantID ( receiverID )
.senderPartyID ( "YOUR_POP_ID_HERE" )
.payload ( payloadElement )
.smpClient ( new SMPClientReadOnly (p6.as4peppol.sender.URL_PROVIDER, receiverID, ESML.DIGIT_TEST) )
.rawResponseConsumer ( new AS4RawResponseConsumerWriteToFile () )
.validationConfiguration ( null )
.buildMessageCallback ( p6.as4peppol.simpleMessageSendLogger() )
.httpClientFactory ( httpSettings )
.sendMessageAndCheckForReceipt ()
println result
}
Receive HTTP POSTed AS4 openPEPPOL Message and Generate Receipt¶
- Route Deployment Script
rest('/public')
.post('/as4')
.to('p6cmb://scripts?platform6.request.action=execute&id=AS4PeppolInbound')
.id('AS4PeppolInbound')
def configMap = [:]
configMap["global.debug"] = "true";
configMap["global.production"] = "false";
configMap["org.apache.wss4j.crypto.merlin.keystore.password"] = "password-in-here"
p6.as4peppol.buildContextConfiguration( configMap ).withCloseable {
def tpl = p6.as4peppol.receive(
p6.pipeline.toStringMap(),
p6.pipeline.getBytes('body'),
{ sbdFilePath, messageId -> println '++++++ RECEIVED openPEPPOL Message\n' + '\nPath: ' + sbdFilePath + '\nMessageID: ' + messageId}
)
// --- Build the Http POST response into the pipeline ---
// Response code
p6.pipeline.put('CamelHttpResponseCode', '' + tpl.first)
// The response or error-message body
p6.pipeline.put('body', tpl.second)
// Additional headers - prefixed with 'p6rest.' to ensure they are part of the final HTTP response
tpl.third.each { key, val ->
p6.pipeline.put( 'p6rest.' + key, val )
}
}
Logging, Debug & Testing¶
The configuration properties global.production
and global.debug
are used to switch between Test and Production certificate usage and control the generation of DEBUG logs
In Addition, the following line should be added to the log4j.properties file to enable DEBUG logging from the Phase4 library:
log4j.logger.com.helger.phase4=DEBUG