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.lucene;
018
019 import java.io.IOException;
020 import java.util.Iterator;
021 import java.util.Set;
022
023 import javax.jbi.JBIException;
024 import javax.jbi.messaging.ExchangeStatus;
025 import javax.jbi.messaging.MessageExchange;
026 import javax.jbi.messaging.MessagingException;
027 import javax.jbi.messaging.NormalizedMessage;
028
029 import org.apache.lucene.document.Document;
030 import org.apache.lucene.document.Field;
031 import org.apache.servicemix.jbi.audit.AbstractAuditor;
032 import org.apache.servicemix.jbi.audit.AuditorException;
033 import org.apache.servicemix.jbi.audit.AuditorMBean;
034 import org.apache.servicemix.jbi.audit.AuditorQueryMBean;
035 import org.apache.servicemix.jbi.event.ExchangeEvent;
036 import org.apache.servicemix.jbi.event.ExchangeListener;
037 import org.apache.servicemix.jbi.jaxp.SourceTransformer;
038
039 /**
040 * Lucene AuditorQuery implementation. It uses Lucene as the indexing mechanism
041 * for searching Exchanges and needs a delegated AuditorMBean to persist
042 * Exchanges.
043 *
044 * The Content of messages are stored as:
045 * - org.apache.servicemix.in.contents
046 * - org.apache.servicemix.out.contents, if exists
047 * - org.apache.servicemix.fault.contents, if exists
048 *
049 * Properties for IN Messages are stored as:
050 * - org.apache.servicemix.in.propertyname
051 * - org.apache.servicemix.out.propertyname, if exists
052 * - org.apache.servicemix.fault.propertyname, if exists
053 *
054 * @author George Gastaldi
055 * @since 2.1
056 * @version $Revision: 11366 $
057 */
058 public class LuceneAuditor extends AbstractAuditor implements AuditorQueryMBean {
059
060 private AuditorMBean delegatedAuditor;
061
062 private LuceneIndexer luceneIndexer = new LuceneIndexer();
063
064 protected void doStart() throws JBIException {
065 super.doStart();
066 if (delegatedAuditor == null) {
067 throw new JBIException("A delegated auditor must be provided");
068 }
069 this.delegatedAuditor.start();
070 }
071
072 protected void doStop() throws JBIException {
073 super.doStop();
074 this.delegatedAuditor.stop();
075 }
076
077 /**
078 * @return Returns the luceneIndexer.
079 */
080 public LuceneIndexer getLuceneIndexer() {
081 return luceneIndexer;
082 }
083
084 /**
085 * @param luceneIndexer
086 * The luceneIndexer to set.
087 */
088 public void setLuceneIndexer(LuceneIndexer luceneIndexer) {
089 this.luceneIndexer = luceneIndexer;
090 }
091
092 /**
093 * @return Returns the delegatedAuditor.
094 */
095 public AuditorMBean getDelegatedAuditor() {
096 return delegatedAuditor;
097 }
098
099 /**
100 * @param delegatedAuditor
101 * The delegatedAuditor to set.
102 */
103 public void setDelegatedAuditor(AuditorMBean delegatedAuditor) {
104 this.delegatedAuditor = delegatedAuditor;
105 if (delegatedAuditor instanceof AbstractAuditor) {
106 ((AbstractAuditor) delegatedAuditor).setAsContainerListener(false);
107 }
108 }
109
110 public int getExchangeCount() throws AuditorException {
111 return this.delegatedAuditor.getExchangeCount();
112 }
113
114 public String[] getExchangeIdsByRange(int fromIndex, int toIndex) throws AuditorException {
115 return this.delegatedAuditor.getExchangeIdsByRange(fromIndex, toIndex);
116 }
117
118 public MessageExchange[] getExchangesByIds(String[] ids) throws AuditorException {
119 return this.delegatedAuditor.getExchangesByIds(ids);
120 }
121
122 public int deleteExchangesByRange(int fromIndex, int toIndex) throws AuditorException {
123 // TODO: Remove ids from Lucene Index
124 return this.delegatedAuditor.deleteExchangesByRange(fromIndex, toIndex);
125 }
126
127 public int deleteExchangesByIds(String[] ids) throws AuditorException {
128 try {
129 this.luceneIndexer.remove(ids);
130 } catch (IOException io) {
131 throw new AuditorException(io);
132 }
133 return this.delegatedAuditor.deleteExchangesByIds(ids);
134 }
135
136 public void exchangeSent(ExchangeEvent event) {
137 MessageExchange exchange = event.getExchange();
138 try {
139 Document doc = createDocument(exchange);
140 this.luceneIndexer.add(doc, exchange.getExchangeId());
141 if (delegatedAuditor instanceof ExchangeListener) {
142 ((ExchangeListener) delegatedAuditor).exchangeSent(event);
143 }
144 } catch (Exception e) {
145 log.error("Error while adding to lucene", e);
146 }
147 }
148
149 public String getDescription() {
150 return "Lucene Auditor";
151 }
152
153 public String[] findExchangesIDsByStatus(ExchangeStatus status) throws AuditorException {
154 String field = "org.apache.servicemix.exchangestatus";
155 return getExchangeIds(field, String.valueOf(status));
156 }
157
158 public String[] findExchangesIDsByMessageContent(String type, String content) throws AuditorException {
159 String field = "org.apache.servicemix." + type + ".contents";
160 return getExchangeIds(field, content);
161 }
162
163 public String[] findExchangesIDsByMessageProperty(String type,
164 String property,
165 String value) throws AuditorException {
166 if (property != null && !property.startsWith("org.apache.servicemix")) {
167 property = "org.apache.servicemix." + type + "." + property;
168 }
169 return getExchangeIds(property, value);
170 }
171
172 protected Document createDocument(MessageExchange me) throws MessagingException {
173 try {
174 // This could be in a separated class (a LuceneDocumentProvider)
175 SourceTransformer st = new SourceTransformer();
176 Document d = new Document();
177 d.add(Field.Keyword("org.apache.servicemix.exchangeid", me.getExchangeId()));
178 d.add(Field.Keyword("org.apache.servicemix.exchangestatus", String.valueOf(me.getStatus())));
179
180 String[] types = {"in", "out", "fault" };
181 for (int i = 0; i < types.length; i++) {
182 String type = types[i];
183 NormalizedMessage nm = me.getMessage(type);
184 if (nm != null) {
185 d.add(Field.UnStored("org.apache.servicemix." + type + ".contents", st.contentToString(nm)));
186 addMessagePropertiesToDocument(nm, d, type);
187 }
188 }
189 return d;
190 } catch (MessagingException mse) {
191 throw mse;
192 } catch (Exception ex) {
193 throw new MessagingException("Error while creating Lucene Document", ex);
194 }
195 }
196
197 protected void addMessagePropertiesToDocument(NormalizedMessage nm,
198 Document document,
199 String type) throws MessagingException {
200 Set propertyNames = nm.getPropertyNames();
201 for (Iterator iter = propertyNames.iterator(); iter.hasNext();) {
202 String propertyName = (String) iter.next();
203 Object value = nm.getProperty(propertyName);
204 if (value instanceof String) {
205 //org.apache.servicemix.out.myproperty
206 document.add(Field.Keyword("org.apache.servicemix." + type + "." + propertyName, String.valueOf(value)));
207 }
208 }
209 }
210
211 public String[] getExchangeIds(String queryContent, String field) throws AuditorException {
212 DefaultLuceneCallback dfc = new DefaultLuceneCallback(queryContent, field);
213 try {
214 return (String[]) luceneIndexer.search(dfc);
215 } catch (IOException e) {
216 throw new AuditorException("Error while getting Exchange IDs", e);
217 }
218 }
219 }