Java tutorial
/** * Copyright (c) 2009-2011 Misys Open Source Solutions (MOSS) and others * * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. * * Contributors: * Misys Open Source Solutions - initial API and implementation * - */ package org.openhealthtools.openexchange.audit; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.xml.bind.JAXBException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openhealthtools.openexchange.actorconfig.net.ConnectionFactory; import org.openhealthtools.openexchange.actorconfig.net.IConnectionDescription; import org.openhealthtools.openexchange.actorconfig.net.StandardConnectionDescription; import org.openhealthtools.openexchange.audit.AuditCodeMappings.ActiveParticipantIds; import org.openhealthtools.openexchange.audit.AuditCodeMappings.AuditEventIds; import org.openhealthtools.openexchange.audit.AuditCodeMappings.AuditSourceType; import org.openhealthtools.openexchange.audit.AuditCodeMappings.AuditTypeCodes; import org.openhealthtools.openexchange.audit.AuditCodeMappings.EventActionCode; import org.openhealthtools.openexchange.audit.AuditCodeMappings.NetworkAccessPointType; import org.openhealthtools.openexchange.audit.AuditCodeMappings.ParticipantObjectIdTypeCode; import org.openhealthtools.openexchange.audit.AuditCodeMappings.ParticipantObjectRoleCode; import org.openhealthtools.openexchange.audit.AuditCodeMappings.ParticipantObjectTypeCode; import org.openhealthtools.openexchange.audit.AuditCodeMappings.SecurityAlertType; import org.openhealthtools.openexchange.audit.AuditCodeMappings.SuccessCode; import org.openhealthtools.openexchange.datamodel.Identifier; import org.openhealthtools.openexchange.datamodel.Patient; import org.openhealthtools.openexchange.datamodel.PatientIdentifier; import org.openhealthtools.openexchange.datamodel.PersonName; import org.openhealthtools.openexchange.utils.LibraryConfig; /** * The base implementation of an audit message class. * * Each IHE Actor requires its own implementation of this base class. * This base class, <em>can</em> be used independently, but that is not * the standard method, and will not be ATNA compliant. <p /> * * Implementations of the audit log class for specific IHE actors must be * instantiated when the actor starts, and stop must be called when the * actor ends, and that instation must not be used again. If the same actor * restarts, a new instance must be made. Calling stop will cause that * specific instance of the audit trail to cease functioning. <p /> * * The following ATNA required messages are currently implemented by the given classes. * * <dl> * <dt>Actor-start-stop * <dd>All actors implement this via the base class. It is generally unnecessary to re implement it for the different actors. Dicom Supp 95 "Application Activity" * <dt>Node-authentication-failure * <dd>Only for secure node TLS failure, not for user login failure. Secure Node Actor. Dicom Sup 95 "Security Alert" * <dt>Patient-record-event * <dd>Patient record created modified or accessed. Document Consumer and Document Source. Dicom Sup 95 "Patient Record" * <dt>Import * <dd>Generally used whenever a document is sent somewhere. Document Source. Dicom Sup 95 "Data Import" * <dt>Export * <dd>Patient record created recieved from somewhere. Document Consumer. Dicom Sup 95 "Data Export" * <dt>Procedure-record-event * <dd>Procedure record created accessed modified or deleted. Document Consumer and Document Source. Dicom Sup 95 "Procedure Record" * <dt>Security-administration * <dd>Change of security roles, user accounts, authentication ability, and other configuration changes. In addition, user authentication, failure, and signoff. Secure Node. Dicom Sup 95 "Security Alert" * </dl> * * @see AuditObjectFactory * @author Josh Flachsbart * @version 1.0 - Oct 27, 2005 */ public class IheAuditTrail implements IAuditTrail { final static Log log = LogFactory.getLog(IheAuditTrail.class); private String actorName; private List<IMessageTransmitter> messengers = null; /** * Handles creation of a logging instance for a given actor. * * Must be called by implementing sub-classes with the appropriate * actor name. <em>Can</em> be used directlly, but will not produce * ATNA compliant messages. * * @param actorName Name of the ATNA actor that this will generate an audit trail for. */ public IheAuditTrail(String actorName, Iterable<IConnectionDescription> repositories) { this.actorName = actorName; // Prepare audit streams: messengers = new ArrayList<IMessageTransmitter>(); for (IConnectionDescription repository : repositories) { AuditTrailDescription description = new AuditTrailDescription(repository); String type = description.getType(); if (type.equalsIgnoreCase(AuditTrailDescription.TLS5425)) { messengers.add(new SyslogTLS5425Messenger(description)); } else if (type.equalsIgnoreCase(AuditTrailDescription.UDP5426)) { messengers.add(new SyslogUdp5426Messenger(description)); } else if (type.equalsIgnoreCase(AuditTrailDescription.BSD)) { messengers.add(new BsdMessenger(description)); //messengers.add(new Log4JMessenger(description)); } else if (type.equalsIgnoreCase(AuditTrailDescription.LOG4J)) { messengers.add(new Log4JMessenger(description)); } // else if (type.equalsIgnoreCase(AuditTrailDescription.RELIABLE)) { // messengers.add(new RSyslogMessenger(description)); // } } } /** * Adds the required elements and format the message in XML and send it. * * To send a message, add additional information that is required by the * message, then call format and log to do the rest. It will add the * audit source id, and active participant id for each audit repository * (since they might have different local info, e.g. if they are in * different RHIOs) and send the message. * p * @param factory * @param requestor */ private void formatAndLog(IMessageTransmitter messenger, AuditObjectFactory factory, boolean requestor, ActiveParticipantIds ourRole, AuditSourceType... sourceTypes) throws JAXBException { AuditTrailDescription desc = messenger.getAuditTrailDescription(); // add the source id: Set<AuditSourceType> auditSourceTypes = new HashSet<AuditSourceType>(); if (sourceTypes != null) { for (AuditSourceType sourceType : sourceTypes) { if (sourceType != null) auditSourceTypes.add(sourceType); } } factory.addAuditSourceId(desc.getEnterpriseSiteId(), desc.getAuditSourceId(), auditSourceTypes); // send the message. factory.sendAuditMessage(); } /////************** DATA HELPER FUNCTIONS ********************* public ActiveParticipant getUser() { ActiveParticipant application = null; LibraryConfig.ILogContext context = LibraryConfig.getInstance().getLogContext(); if (context != null) { application = new ActiveParticipant(); application.role = ActiveParticipantIds.Source; application.setRequestor(true); application.setUserId(context.getUserId()); application.setAltUserId(context.getUserSystem()); application.setUserName(context.getUserName()); application.setAccessPointId(context.getClientAddress()); application.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); application.setAuditSourceType(AuditSourceType.EndUserGui); } return application; } public ActiveParticipant getMedia(ActiveParticipantIds role, String mediaDescription) { ActiveParticipant media = new ActiveParticipant(); media.role = role; media.setRequestor(false); media.setUserId(mediaDescription); media.setAltUserId(null); media.setAccessPointId(null); media.setAuditSourceType(null); return media; } /////************** DICOM MESSAGES ********************* /** * DICOM Supp 95 message A.1.3.1 (p. 15): Application Activity <p /> * * Requires an event id with the parameters below and a single participant * which is the application and a single audit source. The participant and * audit source are provided by the format and log function. <p /> * * Note that we could add the application launcher to this message. * * @param message The type of application activity that it is. Generally start or stop. */ protected void applicationActivity(AuditTypeCodes message) throws JAXBException { for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); factory.setEventIdType(new EventId(AuditEventIds.ApplicationActivity, message, EventActionCode.Execute, SuccessCode.Success)); AuditTrailDescription desc = messenger.getAuditTrailDescription(); ActiveParticipant us = new ActiveParticipant(desc.getFacilityName() + "|" + desc.getApplicationName(), actorName, desc.getIp()); us.setRequestor(false); us.role = ActiveParticipantIds.Application; us.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); factory.addActiveParticipant(us); formatAndLog(messenger, factory, false, ActiveParticipantIds.Application); } } /** * DICOM Supp 95 message A.1.3.11 (p. 29): Patient Record <p /> * * @param source the information about the source of patient. null if not available. * @param patient the information about the patient * @param typeCode the type of message * @param action What they did with the patient record. */ private void patientRecord(ActiveParticipant source, ParticipantObject patient, AuditCodeMappings.AuditTypeCodes typeCode, EventActionCode action) throws JAXBException { for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); if (patient != null) { patient.typeCode = ParticipantObjectTypeCode.Person; patient.role = ParticipantObjectRoleCode.Patient; patient.idTypeCode = ParticipantObjectIdTypeCode.Patient; factory.addParticipantObject(patient); } factory.setEventIdType(new EventId(AuditEventIds.PatientRecord, typeCode, action, SuccessCode.Success)); if (source != null) factory.addActiveParticipant(source); formatAndLog(messenger, factory, false, ActiveParticipantIds.Destination, source.getAuditSourceType()); } } /** * DICOM Supp 95 message :Data Import <p /> * * @param source the information about the source of patient. null if not available. * @param patient the information about the patient * @param typeCode the type of message * @param action What they did with the patient record. */ private void documentImport(ActiveParticipant source, ActiveParticipant destination, ParticipantObject patient, ParticipantObject set, AuditCodeMappings.AuditTypeCodes typeCode) throws JAXBException { for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); if (patient != null) { patient.typeCode = ParticipantObjectTypeCode.Person; patient.role = ParticipantObjectRoleCode.Patient; patient.idTypeCode = ParticipantObjectIdTypeCode.Patient; factory.addParticipantObject(patient); } if (set != null) factory.addParticipantObject(set); factory.setEventIdType( new EventId(AuditEventIds.Import, typeCode, EventActionCode.Create, SuccessCode.Success)); if (source != null) factory.addActiveParticipant(source); if (destination != null) factory.addActiveParticipant(destination); formatAndLog(messenger, factory, false, ActiveParticipantIds.Destination, source.getAuditSourceType()); } } /** * DICOM Supp 95 message A.1.3.4 (p. 19): Data Export <p /> * * This should be used when data leaves control of the system. (E.g. xdm/xdr) <p /> * * Requires an event id with the parameters below. Also requires the * repository as an active participant, if known, and a description of the * patient accessed as a participant object. This is the patient id * and additional information about the document set affected if available. * The SubmisstionSet is required. <p /> * * In addition, a single participant which is the application and a single * audit source. The participant and audit source are provided by the * format and log function. * * @param repository Information about the Repository which is source for Registry. * @param patient the patient related to the Document * @param set the SubmissionSet related to the Document * @param eventActionCode the {@link EventActionCode} */ protected void documentExport(ActiveParticipant repository, ActiveParticipant registry, ParticipantObject patient, ParticipantObject set, AuditCodeMappings.AuditTypeCodes typeCode) throws JAXBException { for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); if (patient != null) { patient.typeCode = ParticipantObjectTypeCode.Person; patient.role = ParticipantObjectRoleCode.Patient; patient.idTypeCode = ParticipantObjectIdTypeCode.Patient; factory.addParticipantObject(patient); } if (set != null) factory.addParticipantObject(set); // Note, AuditTypeCode is not used for data export. factory.setEventIdType( new EventId(AuditEventIds.Export, typeCode, EventActionCode.Read, SuccessCode.Success)); factory.addActiveParticipant(repository); if (registry != null) factory.addActiveParticipant(registry); formatAndLog(messenger, factory, false, ActiveParticipantIds.Destination, repository.getAuditSourceType()); } } /** * DICOM Supp95 message A1.3.13 (p. 33): Query <p /> * * @param source the source information of the application that sends the message * @param patients the patients related to the PDQ Query message * @param query the PIX/PDQ Query information */ private void patientQuery(ActiveParticipant source, Collection<ParticipantObject> patients, ParticipantObject query) throws JAXBException { for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); AuditTypeCodes eventtype = null; if (query.idTypeCode == ParticipantObjectIdTypeCode.PIXQuery) eventtype = AuditCodeMappings.AuditTypeCodes.PixQuery; else eventtype = AuditCodeMappings.AuditTypeCodes.PDQQuery; factory.setEventIdType( new EventId(AuditEventIds.Query, eventtype, EventActionCode.Execute, SuccessCode.Success)); if (source != null) { source.role = ActiveParticipantIds.Source; source.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); source.setRequestor(true); factory.addActiveParticipant(source); } if (patients != null) { for (ParticipantObject patient : patients) { patient.typeCode = ParticipantObjectTypeCode.Person; patient.role = ParticipantObjectRoleCode.Patient; patient.idTypeCode = ParticipantObjectIdTypeCode.Patient; factory.addParticipantObject(patient); } } if (query != null) { query.typeCode = ParticipantObjectTypeCode.SystemObject; query.role = ParticipantObjectRoleCode.Query; factory.addParticipantObject(query); } formatAndLog(messenger, factory, false, ActiveParticipantIds.Destination, source.getAuditSourceType()); } } /** * DICOM Supp 95 message A.1.3.4 (p. 19): Data Export <p /> * * This should be used when data leaves control of the system. (E.g. xdm/xdr) <p /> * * Requires an event id with the parameters below. Also requires the * doctor as an active participant, if known, and a description of the * documents accessed as a participant object. This is the patient id * and additional information about the document affected if available. * The patient info is required. <p /> * * In addition, a single participant which is the application and a single * audit source. The participant and audit source are provided by the * format and log function. * * @param doctor Information about the doctor. null if not available. * @param patient needs to change. This is the information about the patient. * @param action What they did with the patient record. */ protected void dataExport(ActiveParticipant doctor, ActiveParticipant media, ParticipantObject patient) throws JAXBException { for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); // Note, AuditTypeCode is not used for data export. factory.setEventIdType(new EventId(AuditEventIds.Export, (AuditTypeCodes) null, EventActionCode.Read, SuccessCode.Success)); factory.addActiveParticipant(media); factory.addActiveParticipant(doctor); factory.addParticipantObject(patient); formatAndLog(messenger, factory, false, ActiveParticipantIds.Application, doctor.getAuditSourceType()); } } /** * DICOM Supp 95 message A.1.3.4 (p. 19): Data Import <p /> * * This should be used when data was not in control of the system. (E.g. xdm/xdr) <p /> * * Requires an event id with the parameters below. Also requires the * doctor as an active participant, if known, and a description of the * documents accessed as a participant object. This is the patient id * and additional information about the document affected if available. * The patient info is required. <p /> * * In addition, a single participant which is the application and a single * audit source. The participant and audit source are provided by the * format and log function. * * @param doctor Information about the doctor. null if not available. * @param patient needs to change. This is the information about the patient. * @param action What they did with the patient record. */ protected void dataImport(ActiveParticipant doctor, ActiveParticipant media, ParticipantObject patient) throws JAXBException { for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); // Note, AuditTypeCode is not used for data export. factory.setEventIdType(new EventId(AuditEventIds.Import, (AuditTypeCodes) null, EventActionCode.Create, SuccessCode.Success)); factory.addActiveParticipant(doctor); factory.addActiveParticipant(media); factory.addParticipantObject(patient); formatAndLog(messenger, factory, false, ActiveParticipantIds.Application, doctor.getAuditSourceType()); } } /** * DICOM Supp95 message A1.3.14 (p. 34): Security Alert <p /> * * This requires a single event id, the comprimised server if known, the * reporting server (given by format and log) the identity of the reporting * user (assumed to be machine only and therefore unknown) and the offending * participants, if known. We are generally the offending so we just leave * ourselves out since we are already in there, however this is a spot for * improvement in the future. <p /> * * In addition there appears to be a Participant object, but it is poorly defined * and thus is not included here. This should be changed in the future. * * @param success Major error means that security has been comprimised. * Success means an informative alert only. Others mean mitigation was possible. * @param otherServer The comprimised server, if known. */ protected void securityAlert(SuccessCode success, ActiveParticipant otherServer) throws JAXBException { for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); factory.setEventIdType(new EventId(AuditEventIds.SecurityAlert, SecurityAlertType.NodeAuthentication, EventActionCode.Execute, success)); if (otherServer != null) { otherServer.setRequestor(false); factory.addActiveParticipant(otherServer); } // TODO Find out if participant object is required and add it if it is. formatAndLog(messenger, factory, true, ActiveParticipantIds.Application, (otherServer != null) ? otherServer.getAuditSourceType() : null); } } /** * DICOM Supp95 message A1.3.15 (p. 36): User Authentication <p /> * * This message records users logging into the system, as opposed to * security alerts which are for node's authenticating themselves. The single * event id is a login or logout, and whether it succeeded. The user attempting * to authenticate is a required active participant. The enterprise wide * authentication node (e.g. kerberos) is optional, but the actual authentication * node is mandatory though included by format and log. * * @param user User authenticating. Must not be null. * @param isLogin True if user is logging in false if logging out. * @param success Whether the loging was successful. */ protected void userAuthentication(ActiveParticipant user, AuditTypeCodes type, SuccessCode success) throws JAXBException { for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); factory.setEventIdType( new EventId(AuditEventIds.UserAuthentication, type, EventActionCode.Execute, success)); if (user == null) user = getUser(); if (user == null) throw new JAXBException("User not allowed to be null for authentication logging."); user.setRequestor(true); factory.addActiveParticipant(user); formatAndLog(messenger, factory, false, ActiveParticipantIds.Application, user.getAuditSourceType()); } } /////************** ACTUAL MESSAGES ********************* /** Sends actor start log message. Must be called when actor is started. */ public void start() { try { applicationActivity(AuditTypeCodes.ApplicationStart); } catch (JAXBException e) { log.error("Unable to log actor start for: " + actorName, e); } } /** Sends actor stop log message. Must be called when actor is finished. */ public void stop() { try { applicationActivity(AuditTypeCodes.ApplicationStop); } catch (JAXBException e) { log.error("Unable to log actor stop for: " + actorName, e); } } /** * Call when the node fails to authenticate itself with another node. * * Generally you don't log successes since there can be many of those. * * Described in DICOM Supp95 A 1.3.14 as Security Alert. * Described in ITI TF-2 p. 172 as Node-authentication-failure. */ public void nodeAuthenticationFailure(SuccessCode success, IConnectionDescription otherServer) { try { ActiveParticipant otherServerAP = new ActiveParticipant(otherServer); otherServerAP.role = ActiveParticipantIds.Destination; this.securityAlert(success, otherServerAP); } catch (JAXBException e) { log.error("Unable to log node authentication.", e); } } /** * Call when a user authenticates himself. * * Described in DICOM Supp95 A 1.3.15 as User Authentication. * Described in ITI TF-2 p. 172 as Node-authentication-failure. */ public void userLogin(SuccessCode success, ActiveParticipant user) { try { if (user == null) user = getUser(); if (user != null) { user.role = ActiveParticipantIds.Source; user.setRequestor(true); this.userAuthentication(user, AuditTypeCodes.Login, success); } } catch (JAXBException e) { log.error("Unable to log user authentication.", e); } } /** * Call when a user logs out. * * Described in DICOM Supp95 A 1.3.15 as User Authentication. * Described in ITI TF-2 p. 172 as Node-authentication-failure. */ public void userLogout(SuccessCode success, ActiveParticipant user) { try { if (user == null) user = getUser(); user.role = ActiveParticipantIds.Source; this.userAuthentication(user, AuditTypeCodes.Logout, success); } catch (JAXBException e) { log.error("Unable to log user authentication.", e); } } /** * Call when a record is imported from external media. * * Described in DICOM Supp95 A 1.3.5 as Data Import. * * @param patient The patient or document participant object. * @param mediaDesc A string describing the media, e.g. the source e-mail address, or "USB Media", etc... */ public void recordImported(ParticipantObject patient, String mediaDesc) { try { ActiveParticipant user = getUser(); user.role = ActiveParticipantIds.Destination; ActiveParticipant media = getMedia(ActiveParticipantIds.SourceMedia, mediaDesc); patient.role = ParticipantObjectRoleCode.Patient; patient.typeCode = ParticipantObjectTypeCode.Person; this.dataImport(user, media, patient); } catch (JAXBException e) { log.error("Unable to log record import.", e); } } /** * Call when a record is exported to external media. * * Described in DICOM Supp95 A 1.3.4 as Data Export. * * @param patient The patient or document participant object. * @param mediaDesc A string describing the media, e.g. the destination e-mail address, or "USB Media", etc... */ public void recordExported(ParticipantObject patient, String mediaDesc) { try { ActiveParticipant user = getUser(); ActiveParticipant media = getMedia(ActiveParticipantIds.DestinationMedia, mediaDesc); patient.role = ParticipantObjectRoleCode.Patient; patient.typeCode = ParticipantObjectTypeCode.Person; this.dataExport(user, media, patient); } catch (JAXBException e) { log.error("Unable to log record export.", e); } } /////************** ILL DEFINED MESSAGES ********************* /** * Audit Logging of PIX Feed Messages. Call this method when processing PIX Create, * PIX Update and PIX Update Notification messages. * * @param source the source information of the application that sends the message * @param patient the patient related to the PIX Feed message * @param eventActionCode the {@link EventActionCode} */ public void logPixFeed(ActiveParticipant source, ParticipantObject patient, EventActionCode eventActionCode) { try { if (patient == null) //query is required throw new IllegalArgumentException("Required patient is missing"); if (source != null) { source.role = ActiveParticipantIds.Source; source.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); source.setRequestor(true); } patientRecord(source, patient, AuditTypeCodes.PatientIdentityFeed, eventActionCode); } catch (JAXBException e) { log.error("Unable to log patient identity feed", e); } } /** * Audit Logging of PIX Query Messages. Call this method when processing PIX * Query messages. * * @param source the source information of the application that sends the message * @param patient the patient related to the PIX Query message * @param query the PIX Query information */ public void logPixQuery(ActiveParticipant source, ParticipantObject patient, ParticipantObject query) { try { //Participant Objects for Patient Query if (query == null) //query is required throw new IllegalArgumentException("Required query is missing"); query.idTypeCode = ParticipantObjectIdTypeCode.PIXQuery; Collection patients = new ArrayList<ParticipantObject>(); patients.add(patient); patientQuery(source, patients, query); } catch (JAXBException e) { log.error("Unable to log PIX Query", e); } } /** * Audit Logging of PDQ Query Messages. Call this method when processing PDQ * Query messages. * * @param source the source information of the application that sends the message * @param patients the patients related to the PDQ Query message * @param query the PDQ Query information */ public void logPdqQuery(ActiveParticipant source, Collection<ParticipantObject> patients, ParticipantObject query) { try { //Participant Objects for Patient Query if (query == null) //query is required throw new IllegalArgumentException("Required query is missing"); query.idTypeCode = ParticipantObjectIdTypeCode.PDQQuery; patientQuery(source, patients, query); } catch (JAXBException e) { log.error("Unable to log PDQ Query", e); } } /** * Audit Logging of PIX Update Notification Messages. Call this method when processing * PIX Update Notification. * * @param destination the destination information of the application that receives the message * @param patients the patients related to this PIX Update Notification message */ public void logPixUpdateNotification(ActiveParticipant destination, ParticipantObject patient) { try { if (patient == null) //query is required throw new IllegalArgumentException("Required patient is missing"); if (destination != null) { destination.role = ActiveParticipantIds.Destination; destination.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); destination.setRequestor(false); } for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); patient.typeCode = ParticipantObjectTypeCode.Person; patient.role = ParticipantObjectRoleCode.Patient; patient.idTypeCode = ParticipantObjectIdTypeCode.Patient; factory.addParticipantObject(patient); factory.setEventIdType(new EventId(AuditEventIds.PatientRecord, AuditCodeMappings.AuditTypeCodes.PixUpdateNotification, EventActionCode.Read, SuccessCode.Success)); if (destination != null) factory.addActiveParticipant(destination); formatAndLog(messenger, factory, true, ActiveParticipantIds.Source, destination.getAuditSourceType()); } } catch (JAXBException e) { log.error("Unable to log PIX update notification", e); } } /** * Audit Logging of Registry Query Messages. Call this method when * processing Registry Query messages. * * @param source * the source information of the application that sends the * message * @param patients * the patients related to the PDQ Query message * @param query * the Registry Query information * @param isStoredQuery * boolean to define the query type */ public void logRegistryQuery(ActiveParticipant source, ActiveParticipant destination, List<ParticipantObject> patients, ParticipantObject query, boolean isStoredQuery, boolean isMPQ) { try { if (query == null) //query is required throw new IllegalArgumentException("Required query is missing"); //patientQuery(source, patients, query); for (IMessageTransmitter messenger : messengers) { int count = 0; int noOfPatints = 0; do { AuditObjectFactory factory = new AuditObjectFactory(messenger); AuditTypeCodes eventtype = null; ParticipantObject patient = null; if (isMPQ) { eventtype = AuditCodeMappings.AuditTypeCodes.MultiPatientQuery; } else if (isStoredQuery) { eventtype = AuditCodeMappings.AuditTypeCodes.RegistryStoredQuery; } else eventtype = AuditCodeMappings.AuditTypeCodes.RegistrySQLQuery; factory.setEventIdType(new EventId(AuditEventIds.Query, eventtype, EventActionCode.Execute, SuccessCode.Success)); if (source != null) { source.role = ActiveParticipantIds.Source; source.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); source.setRequestor(true); factory.addActiveParticipant(source); } if (destination != null) { destination.role = ActiveParticipantIds.Destination; destination.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); destination.setRequestor(false); factory.addActiveParticipant(destination); } if (patients != null && !patients.isEmpty()) { patient = patients.get(count); noOfPatints = patients.size(); } if (patient != null) { patient.typeCode = ParticipantObjectTypeCode.Person; patient.role = ParticipantObjectRoleCode.Patient; patient.idTypeCode = ParticipantObjectIdTypeCode.Patient; factory.addParticipantObject(patient); } if (query != null) { query.typeCode = ParticipantObjectTypeCode.SystemObject; query.role = ParticipantObjectRoleCode.Query; if (isMPQ) { query.idTypeCode = AuditCodeMappings.ParticipantObjectIdTypeCode.MultiPatientQuery; } else if (isStoredQuery) { query.idTypeCode = AuditCodeMappings.ParticipantObjectIdTypeCode.RegistryStoredQuery; } else { query.idTypeCode = AuditCodeMappings.ParticipantObjectIdTypeCode.RegistrySQLQuery; } factory.addParticipantObject(query); } formatAndLog(messenger, factory, false, ActiveParticipantIds.Destination, source.getAuditSourceType()); count++; } while (count < noOfPatints); } } catch (JAXBException e) { log.error("Unable to log Registry Query", e); } } /** * Audit Logging of Repository Query Messages. Call this method when * processing Repository Query messages. * * @param source * the source information of the application that sends the * message * @param document * the DocumentURI information * @param eventTypeCode * the query type code */ public void logRepositoryQuery(ActiveParticipant source, ActiveParticipant dest, Collection<ParticipantObject> documents, AuditCodeMappings.AuditTypeCodes eventTypeCode) { try { if (documents == null) throw new IllegalArgumentException("Required DocumentURI is missing"); //patientQuery(source, patients, query); for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); factory.setEventIdType(new EventId(AuditEventIds.Export, eventTypeCode, EventActionCode.Read, SuccessCode.Success)); if (source != null) { source.role = ActiveParticipantIds.Source; source.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); source.setRequestor(false); factory.addActiveParticipant(source); } dest.role = ActiveParticipantIds.Destination; dest.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); dest.setRequestor(true); factory.addActiveParticipant(dest); if (documents != null) { for (ParticipantObject document : documents) { document.typeCode = ParticipantObjectTypeCode.SystemObject; document.role = ParticipantObjectRoleCode.Report; document.idTypeCode = AuditCodeMappings.ParticipantObjectIdTypeCode.ReportNumber; factory.addParticipantObject(document); } } formatAndLog(messenger, factory, true, ActiveParticipantIds.Destination, source.getAuditSourceType()); } } catch (JAXBException e) { log.error("Unable to log Repository Query", e); } } /** * Audit Logging of XDS Registry Messages. Call this method when processing * Register Document Set, * * @param source * the source information of the application that sends the * message * @param patient * the patient related to the Document * @param eventActionCode * the {@link EventActionCode} */ public void logDocumentImport(ActiveParticipant source, ActiveParticipant destination, ParticipantObject patient, ParticipantObject set, AuditCodeMappings.AuditTypeCodes typeCode) { try { if (patient == null) throw new IllegalArgumentException("Required patient is missing"); if (set != null) { set.typeCode = ParticipantObjectTypeCode.SystemObject; set.role = ParticipantObjectRoleCode.Job; set.idTypeCode = ParticipantObjectIdTypeCode.SubmissionSet; } if (source != null) { source.role = ActiveParticipantIds.Source; source.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); source.setRequestor(true); } if (destination != null) { destination.role = ActiveParticipantIds.Destination; destination.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); destination.setRequestor(false); } documentImport(source, destination, patient, set, typeCode); } catch (JAXBException e) { log.error("Unable to log Register Document Set", e); } } /** * Audit Logging of XDS Repository Messages. Call this method when sending * data to the registry, * * @param source * the source information of the application that sends the * message * @param patient * the patient related to the Document * @param set * the SubmissionSet related to the Document * @param typeCode * the {@link AuditCodeMappings.AuditTypeCodes} */ public void logDocumentExport(ActiveParticipant source, ActiveParticipant destination, ParticipantObject patient, ParticipantObject set, AuditCodeMappings.AuditTypeCodes typeCode) { try { if (set == null) throw new IllegalArgumentException("Required SubmissionSet is missing"); if (set != null) { set.typeCode = ParticipantObjectTypeCode.SystemObject; set.role = ParticipantObjectRoleCode.Job; set.idTypeCode = ParticipantObjectIdTypeCode.SubmissionSet; } if (source != null) { source.role = ActiveParticipantIds.Source; source.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); source.setRequestor(true); } if (destination != null) { destination.role = ActiveParticipantIds.Destination; destination.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); destination.setRequestor(false); } documentExport(source, destination, patient, set, typeCode); } catch (JAXBException e) { log.error("Unable to log RegisterDocumentSet", e); } } /** * Audit Logging of DSUB Subscribe and Unsubscribe. * @param source, * the source information of the application that sends the message * @param dest * the destination information of the application that receives the message * @param patient * the patient related to subscription * @param query * the subscription query * @param eventTypeCode * the typecode of the event * @param eventactioncode * the action of the event Ex. Create(C), Delete(D) */ public void logDSUBSubcribe(ActiveParticipant source, ActiveParticipant dest, ParticipantObject patient, ParticipantObject query, AuditCodeMappings.AuditTypeCodes eventTypeCode, EventActionCode eventactioncode) { try { if (query == null) throw new IllegalArgumentException("Required Query parameters are missing "); for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); factory.setEventIdType( new EventId(AuditEventIds.Query, eventTypeCode, eventactioncode, SuccessCode.Success)); if (source != null) { source.role = ActiveParticipantIds.Source; source.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); source.setRequestor(true); factory.addActiveParticipant(source); } if (dest != null) { dest.role = ActiveParticipantIds.Destination; dest.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); dest.setRequestor(false); factory.addActiveParticipant(dest); } if (patient != null) { patient.typeCode = ParticipantObjectTypeCode.Person; patient.role = ParticipantObjectRoleCode.Patient; patient.idTypeCode = ParticipantObjectIdTypeCode.Patient; factory.addParticipantObject(patient); } if (query != null) { query.typeCode = ParticipantObjectTypeCode.SystemObject; query.role = ParticipantObjectRoleCode.Query; query.idTypeCode = AuditCodeMappings.ParticipantObjectIdTypeCode.DocumentMetadataSubscribe; factory.addParticipantObject(query); } formatAndLog(messenger, factory, true, ActiveParticipantIds.Destination, source.getAuditSourceType()); } } catch (JAXBException e) { log.error("Unable to log Repository Query", e); } } /** * Audit Logging of DSUB notifications to subscribed Document Metadata Notification Recipients based on received Publish transactions * * @param source, * the source information of the application that sends the message * @param dest * the destination information of the application that receives the message * @param patient * the patient related to subscription * @param query * the subscription query * @param eventTypeCode * the typecode of the event * @param eventactioncode * the action of the event Ex. Create(C), Delete(D) */ public void logDSUBNotify(ActiveParticipant source, ActiveParticipant dest, List<ParticipantObject> documents, AuditCodeMappings.AuditTypeCodes eventTypeCode) { try { for (IMessageTransmitter messenger : messengers) { AuditObjectFactory factory = new AuditObjectFactory(messenger); factory.setEventIdType(new EventId(AuditEventIds.Export, eventTypeCode, EventActionCode.Read, SuccessCode.Success)); if (source != null) { source.role = ActiveParticipantIds.Source; source.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); source.setRequestor(false); factory.addActiveParticipant(source); } if (dest != null) { dest.role = ActiveParticipantIds.Destination; dest.setAccessPointTypeCode(NetworkAccessPointType.IPAddress); dest.setRequestor(true); factory.addActiveParticipant(dest); } if (documents != null) { for (ParticipantObject document : documents) { document.typeCode = ParticipantObjectTypeCode.SystemObject; document.role = ParticipantObjectRoleCode.Report; document.idTypeCode = ParticipantObjectIdTypeCode.ReportNumber; factory.addParticipantObject(document); } } formatAndLog(messenger, factory, true, ActiveParticipantIds.Destination, source.getAuditSourceType()); } } catch (JAXBException e) { log.error("Unable to log Document subscription message", e); } } /////************** TESTING MESSAGES ********************* public static void main(String[] args) { // User // LogContext context = new LogContext(); // context.setClientAddress("10.0.1.101"); // context.setUserId("jones@sunroom.hosp.org"); // context.setUserName("Dr. Jones"); // LogManager.setLogContext(context); // LibraryConfig.getInstance().setLogContext(new TestLogContext()); // Other Server StandardConnectionDescription otherServer = new StandardConnectionDescription(); otherServer.setHostname("kitchen.hosp.org"); otherServer.setName("OtherServer"); // Patient Patient pD = new Patient(); PatientIdentifier pid = new PatientIdentifier(); pid.setId("misys-id-02344321183"); Identifier aa = new Identifier("Test", "1.2.3.4.5", "ISO"); // pid.setAssigningAuthority(aa); pD.addPatientId(new PatientIdentifier()); PersonName name = new PersonName(); name.setFirstName("Susan"); name.setLastName("Formaldehyde"); ParticipantObject patient = new ParticipantObject(pD); //ParticipantObject patient = new ParticipantObject("Susan Formaldehyde", "misys-id-02344321183"); // Repositories ConnectionFactory.loadConnectionDescriptionsFromFile( "J:\\workspace\\connect_refactor_connect\\src\\connectathon\\AuditRepositoryConnections.xml"); ArrayList<IConnectionDescription> repositories = new ArrayList<IConnectionDescription>(); repositories.add(ConnectionFactory.getConnectionDescription("log4j_audittrail")); //repositories.add(new AuditTrailDescription("MISYS", "hosp.misyshealthcare.com", AuditTrailDescription.LOG4J, "10.0.1.101", "Sun room", "Big Hospital")); IheAuditTrail log = new IheAuditTrail("Fake Actor", repositories); log.start(); log.nodeAuthenticationFailure(SuccessCode.MinorFailure, otherServer); log.userLogin(SuccessCode.MinorFailure, log.getUser()); log.userLogin(SuccessCode.Success, log.getUser()); //log.patientQueryIssued(otherServer, patient, true); log.recordImported(patient, "USB Media"); log.recordExported(patient, "USB Media"); log.userLogout(SuccessCode.Success, log.getUser()); log.stop(); } }