/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is part of dcm4che, an implementation of DICOM(TM) in * Java(TM), hosted at https://github.com/gunterze/dcm4che. * * The Initial Developer of the Original Code is * Agfa Healthcare. * Portions created by the Initial Developer are Copyright (C) 2012 * the Initial Developer. All Rights Reserved. * * Contributor(s): * See @authors listed below * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ package org.dcm4che3.net.hl7; import org.dcm4che3.conf.core.api.ConfigurableClass; import org.dcm4che3.conf.core.api.ConfigurableProperty; import org.dcm4che3.conf.core.api.LDAP; import org.dcm4che3.hl7.HL7Exception; import org.dcm4che3.hl7.HL7Segment; import org.dcm4che3.hl7.MLLPConnection; import org.dcm4che3.net.CompatibleConnection; import org.dcm4che3.net.Connection; import org.dcm4che3.net.Device; import org.dcm4che3.net.IncompatibleConnectionException; import java.io.IOException; import java.io.Serializable; import java.net.Socket; import java.security.GeneralSecurityException; import java.util.*; /** * @author Gunter Zeilinger <gunterze@gmail.com> */ @LDAP(objectClasses = "hl7Application") @ConfigurableClass public class HL7Application implements Serializable { private static final long serialVersionUID = -1765110968524548056L; private Device device; @ConfigurableProperty(name = "hl7ApplicationName", label = "HL7 application name", description = "HL7 Application and Facility name (Application^Facility)" ) private String applicationName; @ConfigurableProperty(name = "hl7DefaultCharacterSet", label = "Default character set", description = "Character Set used to decode received messages if not specified by MSH-18, ASCII if absent") private String HL7DefaultCharacterSet; @ConfigurableProperty(name = "dicomInstalled") private Boolean hl7Installed; @ConfigurableProperty(name = "hl7AcceptedSendingApplication", label = "Accepted applications", description = "Application^Facility name of accepted Sending Application(s); any if absent") private Set<String> acceptedSendingApplicationsSet = new LinkedHashSet<String>(); @ConfigurableProperty(name = "hl7AcceptedMessageType", label = "Accepted message types", description = "Message Type(s) (MessageType^TriggerEvent) of accepted messages") private Set<String> acceptedMessageTypesSet = new LinkedHashSet<String>(); @ConfigurableProperty(name = "dicomNetworkConnectionReference", label = "Connections", description = "Which connections are used by this HL7 application", collectionOfReferences = true) private List<Connection> conns = new ArrayList<Connection>(1); @ConfigurableProperty(name = "hl7AppExtensions", isExtensionsProperty = true) private Map<Class<? extends HL7ApplicationExtension>, HL7ApplicationExtension> extensions = new HashMap<Class<? extends HL7ApplicationExtension>, HL7ApplicationExtension>(); private transient HL7MessageListener hl7MessageListener; public HL7Application() { } public HL7Application(String applicationName) { setApplicationName(applicationName); } public final Device getDevice() { return device; } public Map<Class<? extends HL7ApplicationExtension>, HL7ApplicationExtension> getExtensions() { return extensions; } public void setExtensions(Map<Class<? extends HL7ApplicationExtension>, HL7ApplicationExtension> extensions) { this.extensions = extensions; } void setDevice(Device device) { if (device != null) { if (this.device != null) throw new IllegalStateException("already owned by " + this.device.getDeviceName()); for (Connection conn : conns) if (conn.getDevice() != device) throw new IllegalStateException(conn + " not owned by " + device.getDeviceName()); } this.device = device; } public String getApplicationName() { return applicationName; } public void setApplicationName(String name) { if (name.isEmpty()) throw new IllegalArgumentException("name cannot be empty"); this.applicationName = name; } public Set<String> getAcceptedMessageTypesSet() { return acceptedMessageTypesSet; } public void setAcceptedMessageTypesSet(Set<String> acceptedMessageTypesSet) { this.acceptedMessageTypesSet = acceptedMessageTypesSet; } public final String getHL7DefaultCharacterSet() { return HL7DefaultCharacterSet; } public final void setHL7DefaultCharacterSet(String hl7DefaultCharacterSet) { this.HL7DefaultCharacterSet = hl7DefaultCharacterSet; } public Set<String> getAcceptedSendingApplicationsSet() { return acceptedSendingApplicationsSet; } public void setAcceptedSendingApplicationsSet(Set<String> acceptedSendingApplicationsSet) { this.acceptedSendingApplicationsSet = acceptedSendingApplicationsSet; } public String[] getAcceptedSendingApplications() { return acceptedSendingApplicationsSet.toArray( new String[acceptedSendingApplicationsSet.size()]); } public void setAcceptedSendingApplications(String... names) { acceptedSendingApplicationsSet.clear(); Collections.addAll(acceptedSendingApplicationsSet, names); } public String[] getAcceptedMessageTypes() { return acceptedMessageTypesSet.toArray( new String[acceptedMessageTypesSet.size()]); } public void setAcceptedMessageTypes(String... types) { acceptedMessageTypesSet.clear(); Collections.addAll(acceptedMessageTypesSet, types); } public boolean isInstalled() { return device != null && device.isInstalled() && (hl7Installed == null || hl7Installed.booleanValue()); } public Boolean getHl7Installed() { return hl7Installed; } public void setHl7Installed(Boolean hl7Installed) { if (hl7Installed != null && hl7Installed.booleanValue() && device != null && !device.isInstalled()) throw new IllegalStateException("owning device not installed"); this.hl7Installed = hl7Installed; } public HL7MessageListener getHL7MessageListener() { HL7MessageListener listener = hl7MessageListener; if (listener != null) return listener; HL7DeviceExtension hl7Ext = device.getDeviceExtension(HL7DeviceExtension.class); return hl7Ext != null ? hl7Ext.getHL7MessageListener() : null; } public final void setHL7MessageListener(HL7MessageListener listener) { this.hl7MessageListener = listener; } public void addConnection(Connection conn) { if (conn.getProtocol() != Connection.Protocol.HL7) throw new IllegalArgumentException( "protocol != HL7 - " + conn.getProtocol()); if (device != null && device != conn.getDevice()) throw new IllegalStateException(conn + " not contained by " + device.getDeviceName()); conns.add(conn); } public boolean removeConnection(Connection conn) { return conns.remove(conn); } public List<Connection> getConnections() { return conns; } public List<Connection> getConns() { return conns; } public void setConns(List<Connection> conns) { this.conns.clear(); for (Connection conn : conns) addConnection(conn); } byte[] onMessage(Connection conn, Socket s, HL7Segment msh, byte[] msg, int off, int len, int mshlen) throws HL7Exception { if (!(isInstalled() && conns.contains(conn))) throw new HL7Exception(HL7Exception.AR, "Receiving Application not recognized"); if (!(acceptedSendingApplicationsSet.isEmpty() || acceptedSendingApplicationsSet.contains(msh.getSendingApplicationWithFacility()))) throw new HL7Exception(HL7Exception.AR, "Sending Application not recognized"); if (!(acceptedMessageTypesSet.contains("*") || acceptedMessageTypesSet.contains(msh.getMessageType()))) throw new HL7Exception(HL7Exception.AR, "Message Type not supported"); HL7MessageListener listener = getHL7MessageListener(); if (listener == null) throw new HL7Exception(HL7Exception.AE, "No HL7 Message Listener configured"); return listener.onMessage(this, conn, s, msh, msg, off, len, mshlen); } public MLLPConnection connect(Connection remote) throws IOException, IncompatibleConnectionException, GeneralSecurityException { return connect(findCompatibelConnection(remote), remote); } public MLLPConnection connect(HL7Application remote) throws IOException, IncompatibleConnectionException, GeneralSecurityException { CompatibleConnection cc = findCompatibelConnection(remote); return connect(cc.getLocalConnection(), cc.getRemoteConnection()); } public MLLPConnection connect(Connection local, Connection remote) throws IOException, IncompatibleConnectionException, GeneralSecurityException { checkDevice(); checkInstalled(); Socket sock = local.connect(remote); sock.setSoTimeout(local.getResponseTimeout()); return new MLLPConnection(sock); } public CompatibleConnection findCompatibelConnection(HL7Application remote) throws IncompatibleConnectionException { for (Connection remoteConn : remote.conns) if (remoteConn.isInstalled() && remoteConn.isServer()) for (Connection conn : conns) if (conn.isInstalled() && conn.isCompatible(remoteConn)) return new CompatibleConnection(conn, remoteConn); throw new IncompatibleConnectionException( "No compatible connection to " + remote + " available on " + this); } public Connection findCompatibelConnection(Connection remoteConn) throws IncompatibleConnectionException { for (Connection conn : conns) if (conn.isInstalled() && conn.isCompatible(remoteConn)) return conn; throw new IncompatibleConnectionException( "No compatible connection to " + remoteConn + " available on " + this); } private void checkInstalled() { if (!isInstalled()) throw new IllegalStateException("Not installed"); } private void checkDevice() { if (device == null) throw new IllegalStateException("Not attached to Device"); } void reconfigure(HL7Application src) { setHL7ApplicationAttributes(src); device.reconfigureConnections(conns, src.conns); reconfigureHL7ApplicationExtensions(src); } private void reconfigureHL7ApplicationExtensions(HL7Application from) { for (Iterator<Class<? extends HL7ApplicationExtension>> it = extensions.keySet().iterator(); it.hasNext(); ) { if (!from.extensions.containsKey(it.next())) it.remove(); } for (HL7ApplicationExtension src : from.extensions.values()) { Class<? extends HL7ApplicationExtension> clazz = src.getClass(); HL7ApplicationExtension ext = extensions.get(clazz); if (ext == null) try { addHL7ApplicationExtension(ext = clazz.newInstance()); } catch (Exception e) { throw new RuntimeException( "Failed to instantiate " + clazz.getName(), e); } ext.reconfigure(src); } } protected void setHL7ApplicationAttributes(HL7Application src) { setHL7DefaultCharacterSet(src.HL7DefaultCharacterSet); setAcceptedSendingApplications(src.getAcceptedSendingApplications()); setAcceptedMessageTypes(src.getAcceptedMessageTypes()); setHl7Installed(src.hl7Installed); } public void addHL7ApplicationExtension(HL7ApplicationExtension ext) { Class<? extends HL7ApplicationExtension> clazz = ext.getClass(); if (extensions.containsKey(clazz)) throw new IllegalStateException( "already contains AE Extension:" + clazz); ext.setHL7Application(this); extensions.put(clazz, ext); } public boolean removeHL7ApplicationExtension(HL7ApplicationExtension ext) { if (extensions.remove(ext.getClass()) == null) return false; ext.setHL7Application(null); return true; } public Collection<HL7ApplicationExtension> listHL7ApplicationExtensions() { return extensions.values(); } @SuppressWarnings("unchecked") public <T extends HL7ApplicationExtension> T getHL7ApplicationExtension(Class<T> clazz) { return (T) extensions.get(clazz); } }