001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.servicemix.jbi.audit.file;
018
019 import java.io.BufferedOutputStream;
020 import java.io.File;
021 import java.io.FileNotFoundException;
022 import java.io.FileOutputStream;
023 import java.io.IOException;
024 import java.io.OutputStream;
025 import java.io.PrintWriter;
026 import java.text.DateFormat;
027 import java.text.SimpleDateFormat;
028 import java.util.Date;
029
030 import javax.jbi.messaging.ExchangeStatus;
031 import javax.jbi.messaging.MessageExchange;
032 import javax.jbi.messaging.MessagingException;
033 import javax.jbi.messaging.NormalizedMessage;
034 import javax.xml.transform.TransformerException;
035 import javax.xml.transform.stream.StreamResult;
036 import javax.xml.transform.stream.StreamSource;
037
038 import org.apache.commons.logging.Log;
039 import org.apache.commons.logging.LogFactory;
040 import org.apache.servicemix.jbi.audit.AbstractAuditor;
041 import org.apache.servicemix.jbi.audit.AuditorException;
042 import org.apache.servicemix.jbi.event.ExchangeEvent;
043 import org.apache.servicemix.jbi.jaxp.SourceTransformer;
044 import org.apache.servicemix.jbi.util.FileUtil;
045 import org.apache.servicemix.jbi.util.MessageUtil;
046 import org.springframework.beans.factory.InitializingBean;
047
048 /**
049 * Simple implementation of a ServiceMix auditor that stores messages in files in a directory.
050 * Shows usage of {@link TeeInputStream} for auditing {@link StreamSource} message content.
051 *
052 * Currently, the file auditor will only store the message body for ACTIVE exchanges.
053 *
054 * @org.apache.xbean.XBean element="fileAuditor" description="The Auditor of message exchanges to a directory"
055 */
056 public class FileAuditor extends AbstractAuditor implements InitializingBean {
057
058 private static final Log LOG = LogFactory.getLog(FileAuditor.class);
059 private File directory;
060 private FileAuditorStrategy strategy = new FileAuditorStrategyImpl();
061 private boolean autostart = true;
062
063 /**
064 * The directory used for storing the audited messages
065 *
066 * @param directory
067 * the directory
068 */
069 public void setDirectory(File directory) {
070 if (!directory.exists()) {
071 LOG.info("Creating directory " + directory);
072 directory.mkdirs();
073 }
074 this.directory = directory;
075 }
076
077 /**
078 * {@inheritDoc}
079 */
080 public void exchangeSent(ExchangeEvent event) {
081 try {
082 MessageExchange exchange = event.getExchange();
083 if (exchange.getStatus() == ExchangeStatus.ACTIVE) {
084 OutputStream os = getOutputStream(exchange);
085 writeFileHeader(os, exchange);
086 NormalizedMessage in = exchange.getMessage("in");
087 if (StreamSource.class.isAssignableFrom(in.getContent().getClass())) {
088 StreamSource original = (StreamSource) exchange.getMessage("in").getContent();
089 TeeInputStream tis = new TeeInputStream(original.getInputStream(), os);
090 exchange.getMessage("in").setContent(new StreamSource(tis));
091 } else {
092 MessageUtil.enableContentRereadability(in);
093 SourceTransformer transformer = new SourceTransformer();
094 transformer.toResult(in.getContent(), new StreamResult(os));
095 }
096 }
097 } catch (IOException e) {
098 LOG.error(String.format("Error occurred while storing message %s", event.getExchange().getExchangeId()), e);
099 } catch (TransformerException e) {
100 LOG.error(String.format("Error occurred while storing message %s", event.getExchange().getExchangeId()), e);
101 } catch (MessagingException e) {
102 LOG.error(String.format("Error occurred while storing message %s", event.getExchange().getExchangeId()), e);
103 }
104 }
105
106 private void writeFileHeader(OutputStream os, MessageExchange exchange) {
107 MessageExchangeWriter writer = new MessageExchangeWriter(os);
108 writer.writeMessageExchange(exchange);
109 writer.println();
110 writer.println("-- Normalized message (in) --");
111 writer.writeNormalizedMessage(exchange.getMessage("in"));
112 writer.flush();
113 }
114
115 /*
116 * Get the outputstream for writing the message content
117 */
118 private OutputStream getOutputStream(MessageExchange exchange) throws FileNotFoundException {
119 File file = new File(directory, strategy.getFileName(exchange));
120 if (!file.getParentFile().exists()) {
121 file.getParentFile().mkdirs();
122 }
123 return new BufferedOutputStream(new FileOutputStream(file));
124 }
125
126 @Override
127 public int deleteExchangesByIds(String[] ids) throws AuditorException {
128 throw new AuditorException("deleteExchangesById(s) currently unsupported by FileAuditor");
129 }
130
131 @Override
132 public int getExchangeCount() throws AuditorException {
133 return FileUtil.countFilesInDirectory(directory);
134 }
135
136 @Override
137 public String[] getExchangeIdsByRange(int fromIndex, int toIndex) throws AuditorException {
138 throw new AuditorException("getExchangeIdsByRange currently unsupported by FileAuditor");
139 }
140
141 @Override
142 public MessageExchange[] getExchangesByIds(String[] ids) throws AuditorException {
143 throw new AuditorException("getExchangeByIds currently unsupported by FileAuditor");
144 }
145
146 /**
147 * {@inheritDoc}
148 */
149 public String getDescription() {
150 return "File-based auditing service";
151 }
152
153 public void afterPropertiesSet() throws Exception {
154 init(getContainer());
155 if (autostart) {
156 start();
157 } else {
158 stop();
159 }
160 }
161
162 /*
163 * Convenience PrintWriter implementation
164 */
165 private final class MessageExchangeWriter extends PrintWriter {
166
167 private MessageExchangeWriter(OutputStream os) {
168 super(os);
169 }
170
171 private void writeMessageExchange(MessageExchange exchange) {
172 println("-- Exchange " + exchange.getExchangeId() + " --");
173 writeProperty("endpoint", exchange.getEndpoint());
174 writeProperty("MEP", exchange.getPattern());
175 for (Object key : exchange.getPropertyNames()) {
176 writeProperty(key, exchange.getProperty(key.toString()));
177 }
178 }
179
180 private void writeNormalizedMessage(NormalizedMessage message) {
181 for (Object key : message.getPropertyNames()) {
182 writeProperty(key, message.getProperty(key.toString()));
183 }
184 println(); println("- content -");
185 }
186
187
188 private void writeProperty(Object key, Object value) {
189 println(String.format(" %s : %s", key, value));
190 }
191 }
192
193 /*
194 * Default FileAuditorStrategy implementation, writing audit files in a folder per day
195 */
196 private class FileAuditorStrategyImpl implements FileAuditorStrategy {
197
198 private final DateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd");
199
200 public String getFileName(MessageExchange exchange) {
201 return dateformat.format(new Date()) + File.separatorChar + exchange.getExchangeId().replaceAll("[:\\.]", "_");
202 }
203 }
204 }