
com.helger.peppol.bdxrclient.BDXRClientReadOnly Maven / Gradle / Ivy
/**
* Copyright (C) 2015-2019 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* The Original Code is Copyright The PEPPOL project (http://www.peppol.eu)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.helger.peppol.bdxrclient;
import java.net.URI;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.xml.bind.JAXBElement;
import org.apache.http.client.methods.HttpGet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.collection.impl.CommonsArrayList;
import com.helger.commons.collection.impl.ICommonsList;
import com.helger.commons.io.stream.NonBlockingByteArrayInputStream;
import com.helger.peppol.bdxr.EndpointType;
import com.helger.peppol.bdxr.ProcessType;
import com.helger.peppol.bdxr.RedirectType;
import com.helger.peppol.bdxr.ServiceGroupType;
import com.helger.peppol.bdxr.ServiceInformationType;
import com.helger.peppol.bdxr.SignedServiceMetadataType;
import com.helger.peppol.bdxr.marshal.BDXRMarshallerServiceGroupType;
import com.helger.peppol.bdxr.marshal.BDXRMarshallerSignedServiceMetadataType;
import com.helger.peppol.httpclient.AbstractGenericSMPClient;
import com.helger.peppol.httpclient.SMPHttpResponseHandlerSigned;
import com.helger.peppol.httpclient.SMPHttpResponseHandlerUnsigned;
import com.helger.peppol.identifier.generic.doctype.IDocumentTypeIdentifier;
import com.helger.peppol.identifier.generic.participant.IParticipantIdentifier;
import com.helger.peppol.identifier.generic.process.IProcessIdentifier;
import com.helger.peppol.sml.ISMLInfo;
import com.helger.peppol.smp.ISMPTransportProfile;
import com.helger.peppol.smpclient.exception.SMPClientBadRequestException;
import com.helger.peppol.smpclient.exception.SMPClientException;
import com.helger.peppol.smpclient.exception.SMPClientNotFoundException;
import com.helger.peppol.smpclient.exception.SMPClientUnauthorizedException;
import com.helger.peppol.url.IPeppolURLProvider;
import com.helger.peppol.url.PeppolDNSResolutionException;
import com.helger.security.certificate.CertificateHelper;
import com.helger.xsds.xmldsig.X509DataType;
/**
* This class is used for calling the BDXR SMP REST interface. This class only
* contains the read-only methods defined in the SMP specification and nothing
* else.
*
* Note: this class is also licensed under Apache 2 license, as it was not part
* of the original implementation
*
*
* @author Philip Helger
*/
public class BDXRClientReadOnly extends AbstractGenericSMPClient
{
private static final Logger LOGGER = LoggerFactory.getLogger (BDXRClientReadOnly.class);
/**
* Constructor with SML lookup
*
* @param aURLProvider
* The URL provider to be used. May not be null
.
* @param aParticipantIdentifier
* The participant identifier to be used. Required to build the SMP
* access URI.
* @param aSMLInfo
* The SML to be used. Required to build the SMP access URI.
* @throws PeppolDNSResolutionException
* If DNS resolution failed
* @see IPeppolURLProvider#getSMPURIOfParticipant(IParticipantIdentifier,
* ISMLInfo)
*/
public BDXRClientReadOnly (@Nonnull final IPeppolURLProvider aURLProvider,
@Nonnull final IParticipantIdentifier aParticipantIdentifier,
@Nonnull final ISMLInfo aSMLInfo) throws PeppolDNSResolutionException
{
this (aURLProvider.getSMPURIOfParticipant (aParticipantIdentifier, aSMLInfo));
}
/**
* Constructor with SML lookup
*
* @param aURLProvider
* The URL provider to be used. May not be null
.
* @param aParticipantIdentifier
* The participant identifier to be used. Required to build the SMP
* access URI.
* @param sSMLZoneName
* The SML DNS zone name to be used. Required to build the SMP access
* URI. Must end with a trailing dot (".") and may neither be
* null
nor empty to build a correct URL. May not start
* with "http://". Example: sml.peppolcentral.org.
* @throws PeppolDNSResolutionException
* if DNS resolution failed
* @see IPeppolURLProvider#getSMPURIOfParticipant(IParticipantIdentifier,
* String)
*/
public BDXRClientReadOnly (@Nonnull final IPeppolURLProvider aURLProvider,
@Nonnull final IParticipantIdentifier aParticipantIdentifier,
@Nonnull @Nonempty final String sSMLZoneName) throws PeppolDNSResolutionException
{
this (aURLProvider.getSMPURIOfParticipant (aParticipantIdentifier, sSMLZoneName));
}
/**
* Constructor with a direct SMP URL.
* Remember: must be HTTP and using port 80 only!
*
* @param aSMPHost
* The address of the SMP service. Must be port 80 and basic http only
* (no https!). Example: http://smpcompany.company.org
*/
public BDXRClientReadOnly (@Nonnull final URI aSMPHost)
{
super (aSMPHost);
}
/**
* Returns a service group. A service group references to the service
* metadata. This is a specification compliant method.
*
* @param aServiceGroupID
* The service group id corresponding to the service group which one
* wants to get.
* @return The service group. Never null
.
* @throws SMPClientException
* in case something goes wrong
* @throws SMPClientUnauthorizedException
* A HTTP Forbidden was received, should not happen.
* @throws SMPClientNotFoundException
* The service group id did not exist.
* @throws SMPClientBadRequestException
* The request was not well formed.
* @see #getServiceGroupOrNull(IParticipantIdentifier)
*/
@Nonnull
public ServiceGroupType getServiceGroup (@Nonnull final IParticipantIdentifier aServiceGroupID) throws SMPClientException
{
ValueEnforcer.notNull (aServiceGroupID, "ServiceGroupID");
final String sURI = getSMPHostURI () + aServiceGroupID.getURIPercentEncoded ();
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("BDXRClient getServiceGroup@" + sURI);
final HttpGet aRequest = new HttpGet (sURI);
return executeGenericRequest (aRequest,
new SMPHttpResponseHandlerUnsigned <> (new BDXRMarshallerServiceGroupType ()));
}
/**
* Returns a service group. A service group references to the service
* metadata. This is a specification compliant method.
*
* @param aServiceGroupID
* The service group id corresponding to the service group which one
* wants to get.
* @return The service group. May be null
if no such service
* group exists.
* @throws SMPClientException
* in case something goes wrong
* @throws SMPClientUnauthorizedException
* A HTTP Forbidden was received, should not happen.
* @throws SMPClientBadRequestException
* The request was not well formed.
* @see #getServiceGroup(IParticipantIdentifier)
*/
@Nullable
public ServiceGroupType getServiceGroupOrNull (@Nonnull final IParticipantIdentifier aServiceGroupID) throws SMPClientException
{
try
{
return getServiceGroup (aServiceGroupID);
}
catch (final SMPClientNotFoundException ex)
{
return null;
}
}
/**
* Gets a signed service metadata object given by its service group id and its
* document type. This is a specification compliant method.
*
* @param aServiceGroupID
* The service group id of the service metadata to get. May not be
* null
.
* @param aDocumentTypeID
* The document type of the service metadata to get. May not be
* null
.
* @return A signed service metadata object. Never null
.
* @throws SMPClientException
* in case something goes wrong
* @throws SMPClientUnauthorizedException
* A HTTP Forbidden was received, should not happen.
* @throws SMPClientNotFoundException
* The service group id or document type did not exist.
* @throws SMPClientBadRequestException
* The request was not well formed.
* @see #getServiceRegistrationOrNull(IParticipantIdentifier,
* IDocumentTypeIdentifier)
*/
@Nonnull
public SignedServiceMetadataType getServiceRegistration (@Nonnull final IParticipantIdentifier aServiceGroupID,
@Nonnull final IDocumentTypeIdentifier aDocumentTypeID) throws SMPClientException
{
ValueEnforcer.notNull (aServiceGroupID, "ServiceGroupID");
ValueEnforcer.notNull (aDocumentTypeID, "DocumentTypeID");
final String sURI = getSMPHostURI () +
aServiceGroupID.getURIPercentEncoded () +
"/services/" +
aDocumentTypeID.getURIPercentEncoded ();
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("BDXRClient getServiceRegistration@" + sURI);
HttpGet aRequest = new HttpGet (sURI);
SignedServiceMetadataType aMetadata = executeGenericRequest (aRequest,
new SMPHttpResponseHandlerSigned <> (new BDXRMarshallerSignedServiceMetadataType ()).setCheckCertificate (isCheckCertificate ()));
// If the Redirect element is present, then follow 1 redirect.
if (aMetadata.getServiceMetadata () != null && aMetadata.getServiceMetadata ().getRedirect () != null)
{
final RedirectType aRedirect = aMetadata.getServiceMetadata ().getRedirect ();
// Follow the redirect
if (LOGGER.isInfoEnabled ())
LOGGER.info ("Following a redirect from '" + sURI + "' to '" + aRedirect.getHref () + "'");
aRequest = new HttpGet (aRedirect.getHref ());
aMetadata = executeGenericRequest (aRequest,
new SMPHttpResponseHandlerSigned <> (new BDXRMarshallerSignedServiceMetadataType ()).setCheckCertificate (isCheckCertificate ()));
// Check that the certificateUID is correct.
boolean bCertificateSubjectFound = false;
outer: for (final Object aObj : aMetadata.getSignature ().getKeyInfo ().getContent ())
{
final Object aInfoValue = ((JAXBElement >) aObj).getValue ();
if (aInfoValue instanceof X509DataType)
{
final X509DataType aX509Data = (X509DataType) aInfoValue;
for (final Object aX509Obj : aX509Data.getX509IssuerSerialOrX509SKIOrX509SubjectName ())
{
final JAXBElement > aX509element = (JAXBElement >) aX509Obj;
// Find the first subject (of type string)
if (aX509element.getValue () instanceof String)
{
final String sSubject = (String) aX509element.getValue ();
if (!aRedirect.getCertificateUID ().equals (sSubject))
{
throw new SMPClientException ("The certificate UID of the redirect did not match the certificate subject. Subject is '" +
sSubject +
"'. Required certificate UID is '" +
aRedirect.getCertificateUID () +
"'");
}
bCertificateSubjectFound = true;
break outer;
}
}
}
}
if (!bCertificateSubjectFound)
throw new SMPClientException ("The X509 certificate did not contain a certificate subject.");
}
return aMetadata;
}
/**
* Gets a signed service metadata object given by its service group id and its
* document type. This is a specification compliant method.
*
* @param aServiceGroupID
* The service group id of the service metadata to get. May not be
* null
.
* @param aDocumentTypeID
* The document type of the service metadata to get. May not be
* null
.
* @return A signed service metadata object or null
if no such
* registration is present.
* @throws SMPClientException
* in case something goes wrong
* @throws SMPClientUnauthorizedException
* A HTTP Forbidden was received, should not happen.
* @throws SMPClientBadRequestException
* The request was not well formed.
* @see #getServiceRegistration(IParticipantIdentifier,
* IDocumentTypeIdentifier)
*/
@Nullable
public SignedServiceMetadataType getServiceRegistrationOrNull (@Nonnull final IParticipantIdentifier aServiceGroupID,
@Nonnull final IDocumentTypeIdentifier aDocumentTypeID) throws SMPClientException
{
try
{
return getServiceRegistration (aServiceGroupID, aDocumentTypeID);
}
catch (final SMPClientNotFoundException ex)
{
return null;
}
}
/**
* Gets a signed service metadata object given by its service group id and its
* document type. This is a specification compliant method.
*
* @param aServiceGroupID
* The service group id of the service metadata to get. May not be
* null
.
* @param aDocumentTypeID
* The document type of the service metadata to get. May not be
* null
.
* @param aProcessID
* The process ID of the service metadata to get. May not be
* null
.
* @param aTransportProfile
* The transport profile of the service metadata to get. May not be
* null
.
* @return The endpoint from the signed service metadata object or
* null
if no such registration is present.
* @throws SMPClientException
* in case something goes wrong
* @throws SMPClientUnauthorizedException
* A HTTP Forbidden was received, should not happen.
* @throws SMPClientBadRequestException
* The request was not well formed.
* @see #getServiceRegistrationOrNull(IParticipantIdentifier,IDocumentTypeIdentifier)
*/
@Nullable
public EndpointType getEndpoint (@Nonnull final IParticipantIdentifier aServiceGroupID,
@Nonnull final IDocumentTypeIdentifier aDocumentTypeID,
@Nonnull final IProcessIdentifier aProcessID,
@Nonnull final ISMPTransportProfile aTransportProfile) throws SMPClientException
{
ValueEnforcer.notNull (aServiceGroupID, "serviceGroupID");
ValueEnforcer.notNull (aDocumentTypeID, "DocumentTypeID");
ValueEnforcer.notNull (aProcessID, "ProcessID");
ValueEnforcer.notNull (aTransportProfile, "TransportProfile");
// Get meta data for participant/documentType
final SignedServiceMetadataType aSignedServiceMetadata = getServiceRegistrationOrNull (aServiceGroupID,
aDocumentTypeID);
return aSignedServiceMetadata == null ? null : getEndpoint (aSignedServiceMetadata, aProcessID, aTransportProfile);
}
/**
* Extract the Endpoint from the signedServiceMetadata that matches the passed
* process ID and the optional required transport profile.
*
* @param aSignedServiceMetadata
* The signed service meta data object (e.g. from a call to
* {@link #getServiceRegistrationOrNull(IParticipantIdentifier, IDocumentTypeIdentifier)}
* . May not be null
.
* @param aProcessID
* The process identifier to be looked up. May not be null
* .
* @param aTransportProfile
* The required transport profile to be used. May not be
* null
.
* @return null
if no matching endpoint was found
*/
@Nullable
public static EndpointType getEndpoint (@Nonnull final SignedServiceMetadataType aSignedServiceMetadata,
@Nonnull final IProcessIdentifier aProcessID,
@Nonnull final ISMPTransportProfile aTransportProfile)
{
ValueEnforcer.notNull (aSignedServiceMetadata, "SignedServiceMetadata");
ValueEnforcer.notNull (aSignedServiceMetadata.getServiceMetadata (), "SignedServiceMetadata.ServiceMetadata");
if (aSignedServiceMetadata.getServiceMetadata ().getServiceInformation () == null)
{
// It seems to be a redirect and not service information
return null;
}
ValueEnforcer.notNull (aSignedServiceMetadata.getServiceMetadata ().getServiceInformation ().getProcessList (),
"SignedServiceMetadata.ServiceMetadata.ServiceInformation.ProcessList");
ValueEnforcer.notNull (aProcessID, "ProcessID");
ValueEnforcer.notNull (aTransportProfile, "TransportProfile");
// Iterate all processes
final ServiceInformationType aServiceInformation = aSignedServiceMetadata.getServiceMetadata ()
.getServiceInformation ();
if (aServiceInformation != null)
{
// Okay, it's not a redirect
for (final ProcessType aProcessType : aServiceInformation.getProcessList ().getProcess ())
{
// Matches the requested one?
if (aProcessType.getProcessIdentifier ().equals (aProcessID))
{
// Filter endpoints by required transport profile
final ICommonsList aRelevantEndpoints = new CommonsArrayList <> ();
for (final EndpointType aEndpoint : aProcessType.getServiceEndpointList ().getEndpoint ())
if (aTransportProfile.getID ().equals (aEndpoint.getTransportProfile ()))
aRelevantEndpoints.add (aEndpoint);
if (aRelevantEndpoints.size () != 1)
{
if (LOGGER.isWarnEnabled ())
LOGGER.warn ("Found " +
aRelevantEndpoints.size () +
" endpoints for process " +
aProcessID +
" and transport profile " +
aTransportProfile.getID () +
(aRelevantEndpoints.isEmpty () ? ""
: ": " +
aRelevantEndpoints.toString () +
" - using the first one"));
}
// Use the first endpoint or null
return aRelevantEndpoints.getFirst ();
}
}
}
return null;
}
@Nullable
public static String getEndpointAddress (@Nullable final EndpointType aEndpoint)
{
return aEndpoint == null ? null : aEndpoint.getEndpointURI ();
}
@Nullable
public String getEndpointAddress (@Nonnull final IParticipantIdentifier aServiceGroupID,
@Nonnull final IDocumentTypeIdentifier aDocumentTypeID,
@Nonnull final IProcessIdentifier aProcessID,
@Nonnull final ISMPTransportProfile aTransportProfile) throws SMPClientException
{
final EndpointType aEndpoint = getEndpoint (aServiceGroupID, aDocumentTypeID, aProcessID, aTransportProfile);
return getEndpointAddress (aEndpoint);
}
@Nullable
public static byte [] getEndpointCertificateString (@Nullable final EndpointType aEndpoint)
{
return aEndpoint == null ? null : aEndpoint.getCertificate ();
}
@Nullable
public byte [] getEndpointCertificateString (@Nonnull final IParticipantIdentifier aServiceGroupID,
@Nonnull final IDocumentTypeIdentifier aDocumentTypeID,
@Nonnull final IProcessIdentifier aProcessID,
@Nonnull final ISMPTransportProfile aTransportProfile) throws SMPClientException
{
final EndpointType aEndpoint = getEndpoint (aServiceGroupID, aDocumentTypeID, aProcessID, aTransportProfile);
return getEndpointCertificateString (aEndpoint);
}
@Nullable
public X509Certificate getEndpointCertificate (@Nonnull final IParticipantIdentifier aServiceGroupID,
@Nonnull final IDocumentTypeIdentifier aDocumentTypeID,
@Nonnull final IProcessIdentifier aProcessID,
@Nonnull final ISMPTransportProfile aTransportProfile) throws SMPClientException,
CertificateException
{
final byte [] aCertString = getEndpointCertificateString (aServiceGroupID,
aDocumentTypeID,
aProcessID,
aTransportProfile);
if (aCertString == null)
return null;
return (X509Certificate) CertificateHelper.getX509CertificateFactory ()
.generateCertificate (new NonBlockingByteArrayInputStream (aCertString));
}
@Nullable
public static X509Certificate getEndpointCertificate (@Nullable final EndpointType aEndpoint) throws CertificateException
{
final byte [] aCertString = getEndpointCertificateString (aEndpoint);
if (aCertString == null)
return null;
return (X509Certificate) CertificateHelper.getX509CertificateFactory ()
.generateCertificate (new NonBlockingByteArrayInputStream (aCertString));
}
/**
* Returns a service group. A service group references to the service
* metadata.
*
* @param aURLProvider
* The URL provider to be used. May not be null
.
* @param aSMLInfo
* The SML object to be used
* @param aServiceGroupID
* The service group id corresponding to the service group which one
* wants to get.
* @return The service group
* @throws SMPClientException
* in case something goes wrong
* @throws SMPClientUnauthorizedException
* A HTTP Forbidden was received, should not happen.
* @throws SMPClientNotFoundException
* The service group id did not exist.
* @throws SMPClientBadRequestException
* The request was not well formed.
* @throws PeppolDNSResolutionException
* if DNS resolution fails
*/
@Nonnull
public static ServiceGroupType getServiceGroupByDNS (@Nonnull final IPeppolURLProvider aURLProvider,
@Nonnull final ISMLInfo aSMLInfo,
@Nonnull final IParticipantIdentifier aServiceGroupID) throws SMPClientException,
PeppolDNSResolutionException
{
return new BDXRClientReadOnly (aURLProvider, aServiceGroupID, aSMLInfo).getServiceGroup (aServiceGroupID);
}
/**
* Gets a signed service metadata object given by its service group id and its
* document type.
*
* @param aURLProvider
* The URL provider to be used. May not be null
.
* @param aSMLInfo
* The SML object to be used
* @param aServiceGroupID
* The service group id of the service metadata to get.
* @param aDocumentTypeID
* The document type of the service metadata to get.
* @return A signed service metadata object.
* @throws SMPClientException
* in case something goes wrong
* @throws SMPClientUnauthorizedException
* A HTTP Forbidden was received, should not happen.
* @throws SMPClientNotFoundException
* The service group id or document type did not exist.
* @throws SMPClientBadRequestException
* The request was not well formed.
* @throws PeppolDNSResolutionException
* if DNS resolution fails
*/
@Nonnull
public static SignedServiceMetadataType getServiceRegistrationByDNS (@Nonnull final IPeppolURLProvider aURLProvider,
@Nonnull final ISMLInfo aSMLInfo,
@Nonnull final IParticipantIdentifier aServiceGroupID,
@Nonnull final IDocumentTypeIdentifier aDocumentTypeID) throws SMPClientException,
PeppolDNSResolutionException
{
return new BDXRClientReadOnly (aURLProvider, aServiceGroupID, aSMLInfo).getServiceRegistration (aServiceGroupID,
aDocumentTypeID);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy