001/* 002 * jPOS Project [http://jpos.org] 003 * Copyright (C) 2000-2026 jPOS Software SRL 004 * 005 * This program is free software: you can redistribute it and/or modify 006 * it under the terms of the GNU Affero General Public License as 007 * published by the Free Software Foundation, either version 3 of the 008 * License, or (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU Affero General Public License for more details. 014 * 015 * You should have received a copy of the GNU Affero General Public License 016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 017 */ 018 019package org.jpos.util; 020 021import org.jdom2.Element; 022import org.jpos.core.Configurable; 023import org.jpos.core.ConfigurationException; 024import org.jpos.core.XmlConfigurable; 025import org.jpos.q2.SimpleConfigurationFactory; 026import org.jpos.util.function.ByteArrayMapper; 027import org.jpos.util.function.LogEventMapper; 028 029import java.io.ByteArrayOutputStream; 030import java.io.IOException; 031import java.io.PrintStream; 032import java.util.ArrayList; 033import java.util.List; 034 035/** 036 * LogEventWriter that can be configured with event and output mappers to modify 037 * the events before writing to output stream and modify the output stream before writing 038 * to the final destination respectfully. 039 * 040 * Example configuration: <br> 041 * <pre>{@code 042 * <writer class="org.jpos.util.MappingLogEventWriter"> 043 * <event-mapper class="..."> 044 * <property.... /> 045 * </event-mapper> 046 * <event-mapper class="..."/> 047 * <output-mapper class="..."> 048 * <property.... /> 049 * </output-mapper> 050 * <output-mapper class="..."/> 051 * </writer> 052 * }</pre> 053 * 054 * @author Alwyn Schoeman 055 * @since 2.1.4 056 */ 057public class MappingLogEventWriter extends BaseLogEventWriter implements XmlConfigurable { 058 List<LogEventMapper> eventMappers; 059 List<ByteArrayMapper> outputMappers; 060 ByteArrayOutputStream captureOutputStream; 061 PrintStream capturePrintStream; 062 063 @Override 064 public void setPrintStream(PrintStream p) { 065 super.setPrintStream(p); 066 if (p != null && capturePrintStream == null) { 067 configureCaptureStreams(); 068 } 069 } 070 071 @Override 072 public synchronized void close() { 073 if (capturePrintStream != null) { 074 capturePrintStream.close(); 075 capturePrintStream = null; 076 captureOutputStream = null; 077 } 078 super.close(); 079 } 080 081 @Override 082 public void write(LogEvent ev) { 083 ev = mapEvents(ev); 084 if (capturePrintStream != null) { 085 writeToCaptureStream(ev); 086 try { 087 byte[] output = mapOutput(captureOutputStream.toByteArray()); 088 p.write(output); 089 } catch (IOException e) { 090 e.printStackTrace(p); 091 } finally { 092 p.flush(); 093 captureOutputStream.reset(); 094 } 095 } else { 096 delegateWriteToSuper(ev); 097 } 098 } 099 100 @Override 101 public void setConfiguration(Element e) throws ConfigurationException { 102 configureEventMappers(e); 103 configureOutputMappers(e); 104 } 105 106 protected void configureCaptureStreams() { 107 if (outputMappers != null && !outputMappers.isEmpty()) { 108 captureOutputStream = new ByteArrayOutputStream(); 109 capturePrintStream = new PrintStream(captureOutputStream); 110 } 111 } 112 113 protected void configureEventMappers(Element e) throws ConfigurationException { 114 List<Element> eventMappers = e.getChildren("event-mapper"); 115 LogEventMapper mapper; 116 for (Element em : eventMappers) { 117 String clazz = em.getAttributeValue("class"); 118 if (clazz != null) { 119 try { 120 mapper = (LogEventMapper) Class.forName(clazz).newInstance(); 121 } catch (Exception ex) { 122 throw new ConfigurationException(ex); 123 } 124 if (mapper != null) { 125 if (mapper instanceof Configurable) { 126 SimpleConfigurationFactory factory = new SimpleConfigurationFactory(); 127 ((Configurable) mapper).setConfiguration(factory.getConfiguration(em)); 128 } 129 if (mapper instanceof XmlConfigurable) { 130 ((XmlConfigurable) mapper).setConfiguration(em); 131 } 132 if (this.eventMappers == null) { 133 this.eventMappers = new ArrayList<>(); 134 } 135 this.eventMappers.add(mapper); 136 } 137 } 138 } 139 } 140 141 protected void configureOutputMappers(Element e) throws ConfigurationException { 142 List<Element> outputMappers = e.getChildren("output-mapper"); 143 ByteArrayMapper mapper; 144 for (Element em : outputMappers) { 145 String clazz = em.getAttributeValue("class"); 146 if (clazz != null) { 147 try { 148 mapper = (ByteArrayMapper) Class.forName(clazz).newInstance(); 149 } catch (Exception ex) { 150 throw new ConfigurationException(ex); 151 } 152 if (mapper != null) { 153 if (mapper instanceof Configurable) { 154 SimpleConfigurationFactory factory = new SimpleConfigurationFactory(); 155 ((Configurable) mapper).setConfiguration(factory.getConfiguration(em)); 156 } 157 if (mapper instanceof XmlConfigurable) { 158 ((XmlConfigurable) mapper).setConfiguration(em); 159 } 160 if (this.outputMappers == null) { 161 this.outputMappers = new ArrayList<>(); 162 } 163 this.outputMappers.add(mapper); 164 } 165 } 166 } 167 } 168 169 protected LogEvent mapEvents(LogEvent ev) { 170 if (eventMappers != null) { 171 for (LogEventMapper mapper : eventMappers) { 172 ev = mapper.apply(ev); 173 } 174 } 175 return ev; 176 } 177 178 protected byte[] mapOutput(byte[] output) { 179 if (outputMappers != null) { 180 for (ByteArrayMapper mapper : outputMappers) { 181 output = mapper.apply(output); 182 } 183 } 184 return output; 185 } 186 187 /** 188 * This method exists and is used so that we can verify the order of instructions 189 * during a call to write in unit tests. 190 * 191 * @param ev LogEvent to write. 192 */ 193 protected void delegateWriteToSuper(LogEvent ev) { 194 super.write(ev); 195 } 196 197 /** 198 * Write to capture print stream when defined. 199 * @param ev LogEvent to write. 200 */ 201 protected void writeToCaptureStream(LogEvent ev) { 202 if (capturePrintStream != null && ev != null) { 203 ev.dump(capturePrintStream, ""); 204 capturePrintStream.flush(); 205 } 206 } 207}