How to create a server

Description

This tutorial will show you how to create a Dicom SCP server with dicom4j.

to make a server, you have to follow this steps:

First step: extends DefaultAssociateRequestHandler

In this step you have to extendsDefaultAssociateRequestHandler in order to implements which SOP classes your server will support, and accept or reject Requested Sop class.

In order to Accept or Refuse SOP classes you have to Override requestReceived methods.

In this method, you have to return an AssociateResponse related to the AssociateRequest. So you have to create a default response, and loop to manage all PresentationContextItem. For all PresentationContextItem you have to add a PresentationContext with the reason (accept or refuse) and the TransferSyntax to use.

in the following example we only acccept Verification SOP class and accept the first TransfertSyntax of the request

@Override
public AssociateResponse requestReceived(Association aAssociation, AssociateRequest aAssociateRequest) {
	AssociateResponse lReponse = createDefaultResponse(aAssociateRequest);
	Iterator lPres = aAssociateRequest.getPresentationIterator();
	while (lPres.hasNext()) {
		PresentationContextItemRQ lPresRQ = (PresentationContextItemRQ) lPres.next();
		if (lPresRQ.getAbstractSyntax().equals(SOPClass.Verification.getUID())) {
			lReponse.addPresentationContext(lPresRQ.getID(),
					NetworkStaticProperties.PresentationContextReasons.ACCEPTANCE,
					lPresRQ.getransferSyntax(0));
		} else {
			lReponse.addPresentationContext(
							lPresRQ.getID(),
							NetworkStaticProperties.PresentationContextReasons.USER_REJECTION,
							lPresRQ.getransferSyntax(0));
		}
	}
	return lReponse; // we return the created Response
}

For more details on Association negotiation, you can check dicom norms (part 8).

Second step: extends DefaultAssociationListener

In this step you have to extendsDefaultAssociationListener in order to handle all association events. The two main events to handle / methods to ovverride are:

the exceptionCaught is used when any-kind of Exception occurs during the communication. You have to manage want to do is this case (ex: log thre message, send A-Abort)

in the following example we print the error's message and try to send User A-Abort

@Override
public void exceptionCaught(Association association, Throwable cause) {
	System.err.println( cause.getMessage() );
	try {
		association.sendAbort( AssociateAbort.ServiceUserAbort );				
	} catch (Exception e) {
		System.err.println( cause.getMessage() );
	}
}

the messageReceived is the most important methods to implements a server. This methods is called everytime a Dimse message was received from the remote peer throw the Assocation. To manage the message, you have to check the kind of message (by using instanceof ) and do your stuff depending of the message and the message's SOP class.

In the following example, we only manage C-Echo-Req and send abort for others types of messages.

@Override
public void messageReceived(Association aAssociation,	byte aPresentationContextID, DimseMessage aMessage) throws Exception {
	if (aMessage instanceof CEchoRequestMessage ) {
		CEchoRequestMessage lRequest = (CEchoRequestMessage) aMessage;
		int lMessageID = lRequest.getMessageID();
		String lSOPClassUID = lRequest.getAffectedSOPClassUID();
		CEchoResponseMessage lreponse = MessageFactory.newCEchoResponse(lMessageID, lSOPClassUID, DimseStatus.Success); 
		aAssociation.sendMessage(aPresentationContextID, lreponse);
	} else {
		association.sendAbort( AssociateAbort.ServiceUserAbort );	
	}		
}

Third step: create a server class

In order to accept association, you can use the AssociationAcceptor. Te acceptor use the Configuration Class to set the AssociationAcceptor and AssociationListener to used (defined on step 1 and 2).

In the following example, we use the acceptor on port 104.

public static class SCPWorker extends Thread {

	public SCPWorker() {
		super();
	}

	@Override
	public void run() {
		TransportAcceptorConfiguration lTransportAcceptorConfiguration = new TransportAcceptorConfiguration();
		TransportAcceptor lAcceptor = new TransportAcceptor();
		lAcceptor.setConfiguration(lTransportAcceptorConfiguration);
		AssociationAcceptorConfiguration lAssocConfig = new AssociationAcceptorConfigurationImpl();
		lAssocConfig.setAssociateRequestHandler(new CEchoSCPAssociateRequestHandler());
		lAssocConfig.setAssociationListener(new CEchoSCPAssociationListener());
		lAssocConfig.setTransportAcceptor(lAcceptor);
		AssociationAcceptor lAssoc = new AssociationAcceptorImpl();
		lAssoc.setConfiguration(lAssocConfig);
		try {
			lAssoc.bind( 104 );
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Note: The acceptor must be launched on a thread to not block the main thread.

Back