blob: 8b099eae90e8c1e21fc00913f21f3c6aec61ce0e [file] [log] [blame]
Richard S. Hallddf2e142009-09-30 17:03:45 +00001/*
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +00002 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
Richard S. Hall44002cf2009-02-11 21:47:26 +000019package org.apache.felix.log;
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +000020
21import java.util.Enumeration;
22
23import org.osgi.framework.BundleEvent;
24import org.osgi.framework.BundleListener;
25import org.osgi.framework.FrameworkEvent;
26import org.osgi.framework.FrameworkListener;
27import org.osgi.framework.ServiceEvent;
28import org.osgi.framework.ServiceListener;
29import org.osgi.service.log.LogEntry;
30import org.osgi.service.log.LogListener;
31import org.osgi.service.log.LogService;
32
33/**
34 * Class used to represent the log. This class is used by the implementations
35 * of both the {@link org.osgi.service.log.LogService} interface and the
36 * {@link org.osgi.service.log.LogReaderService} to access the log.
37 * @see org.osgi.service.log.LogService
38 * @see org.osgi.service.log.LogReaderService
39 */
Richard S. Hallddf2e142009-09-30 17:03:45 +000040final class Log implements BundleListener, FrameworkListener, ServiceListener
41{
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +000042 /** The first log entry. */
43 private LogNode m_head;
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +000044 /** The last log entry. */
45 private LogNode m_tail;
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +000046 /** The log size. */
47 private int m_size;
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +000048 /** The log listener thread. */
49 private LogListenerThread listenerThread;
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +000050 /** The maximum size for the log. */
51 private final int m_maxSize;
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +000052 /** Whether or not to store debug messages. */
53 private final boolean m_storeDebug;
54
55 /**
56 * Create a new instance.
57 * @param maxSize the maximum size for the log
58 * @param storeDebug whether or not to store debug messages
59 */
Richard S. Hallddf2e142009-09-30 17:03:45 +000060 Log(final int maxSize, final boolean storeDebug)
61 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +000062 this.m_maxSize = maxSize;
63 this.m_storeDebug = storeDebug;
64 }
65
66 /**
67 * Close the log.
68 */
Richard S. Hallddf2e142009-09-30 17:03:45 +000069 void close()
70 {
71 if (listenerThread != null)
72 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +000073 listenerThread.shutdown();
74 listenerThread = null;
75 }
76
77 m_head = null;
78 m_tail = null;
79 m_size = 0;
80 }
81
82 /**
83 * Adds the entry to the log.
84 * @param entry the entry to add to the log
85 */
Richard S. Hallddf2e142009-09-30 17:03:45 +000086 synchronized void addEntry(final LogEntry entry)
87 {
88 if (m_maxSize != 0)
89 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +000090 // add the entry to the historic log
Richard S. Hallddf2e142009-09-30 17:03:45 +000091 if (m_storeDebug || entry.getLevel() != LogService.LOG_DEBUG)
92 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +000093 // create a new node for the entry
94 LogNode node = new LogNode(entry);
95
96 // add to the front of the linked list
97 node.setNextNode(m_head);
Richard S. Hallddf2e142009-09-30 17:03:45 +000098 if (m_head != null)
99 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000100 m_head.setPreviousNode(node);
101 }
102
103 // and store the node
104 m_head = node;
105
106 // bump the size of the list
107 ++m_size;
108
109 // if no tail node - add the node to the tail
Richard S. Hallddf2e142009-09-30 17:03:45 +0000110 if (m_tail == null)
111 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000112 m_tail = node;
113 }
114 }
115
116 // ensure the historic log doesn't grow beyond a certain size
Richard S. Hallddf2e142009-09-30 17:03:45 +0000117 if (m_maxSize != -1)
118 {
119 if (m_size > m_maxSize)
120 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000121 LogNode last = m_tail.getPreviousNode();
122 last.setNextNode(null);
123 m_tail = last;
124 --m_size;
125 }
126 }
127 }
128
129 // notify any listeners
Richard S. Hallddf2e142009-09-30 17:03:45 +0000130 if (listenerThread != null)
131 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000132 listenerThread.addEntry(entry);
133 }
134 }
135
136 /**
137 * Add a listener to the log.
138 * @param listener the log listener to subscribe
139 */
Richard S. Hallddf2e142009-09-30 17:03:45 +0000140 synchronized void addListener(final LogListener listener)
141 {
142 if (listenerThread == null)
143 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000144 // create a new listener thread if necessary:
145 // the listener thread only runs if there are any registered listeners
146 listenerThread = new LogListenerThread();
147 listenerThread.start();
148 }
149 listenerThread.addListener(listener);
150 }
151
152 /**
153 * Remove a listener from the log.
154 * @param listener the log listener to unsubscribe
155 */
Richard S. Hallddf2e142009-09-30 17:03:45 +0000156 synchronized void removeListener(final LogListener listener)
157 {
158 if (listenerThread != null)
159 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000160 listenerThread.removeListener(listener);
161
162 // shutdown the thread if there are no listeners
Richard S. Hallddf2e142009-09-30 17:03:45 +0000163 if (listenerThread.getListenerCount() == 0)
164 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000165 listenerThread.shutdown();
166 listenerThread = null;
167 }
168 }
169 }
170
171 /**
172 * Returns an enumeration of all the entries in the log most recent first.
173 * @return an enumeration of all the entries in the log most recent first
174 */
Richard S. Hallddf2e142009-09-30 17:03:45 +0000175 synchronized Enumeration getEntries()
176 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000177 return new LogNodeEnumeration(m_head, m_tail);
178 }
179
180 /** The messages returned for the framework events. */
Richard S. Hallddf2e142009-09-30 17:03:45 +0000181 private static final String[] FRAMEWORK_EVENT_MESSAGES =
182 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000183 "FrameworkEvent STARTED",
184 "FrameworkEvent ERROR",
185 "FrameworkEvent PACKAGES REFRESHED",
186 "FrameworkEvent STARTLEVEL CHANGED",
187 "FrameworkEvent WARNING",
188 "FrameworkEvent INFO"
189 };
190
191 /**
192 * Called when a framework event occurs.
193 * @param event the event that occured
194 */
Richard S. Hallddf2e142009-09-30 17:03:45 +0000195 public void frameworkEvent(final FrameworkEvent event)
196 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000197 int eventType = event.getType();
198 String message = null;
199
Richard S. Hallddf2e142009-09-30 17:03:45 +0000200 for (int i = 0; message == null && i < FRAMEWORK_EVENT_MESSAGES.length; ++i)
201 {
202 if (eventType >> i == 1)
203 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000204 message = FRAMEWORK_EVENT_MESSAGES[i];
205 }
206 }
207
208 LogEntry entry = new LogEntryImpl(event.getBundle(),
Richard S. Hallddf2e142009-09-30 17:03:45 +0000209 null,
210 (eventType == FrameworkEvent.ERROR) ? LogService.LOG_ERROR : LogService.LOG_INFO,
211 message,
212 event.getThrowable());
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000213
214 addEntry(entry);
215 }
216
217 /** The messages returned for the bundle events. */
Richard S. Hallddf2e142009-09-30 17:03:45 +0000218 private static final String[] BUNDLE_EVENT_MESSAGES =
219 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000220 "BundleEvent INSTALLED",
221 "BundleEvent STARTED",
222 "BundleEvent STOPPED",
223 "BundleEvent UPDATED",
224 "BundleEvent UNINSTALLED",
225 "BundleEvent RESOLVED",
226 "BundleEvent UNRESOLVED"
227 };
228
229 /**
230 * Called when a bundle event occurs.
231 * @param event the event that occured
232 */
Richard S. Hallddf2e142009-09-30 17:03:45 +0000233 public void bundleChanged(final BundleEvent event)
234 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000235 int eventType = event.getType();
236 String message = null;
237
Richard S. Hallddf2e142009-09-30 17:03:45 +0000238 for (int i = 0; message == null && i < BUNDLE_EVENT_MESSAGES.length; ++i)
239 {
240 if (eventType >> i == 1)
241 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000242 message = BUNDLE_EVENT_MESSAGES[i];
243 }
244 }
245
Richard S. Hallddf2e142009-09-30 17:03:45 +0000246 if (message != null)
247 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000248 LogEntry entry = new LogEntryImpl(event.getBundle(),
Richard S. Hallddf2e142009-09-30 17:03:45 +0000249 null,
250 LogService.LOG_INFO,
251 message,
252 null);
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000253
254 addEntry(entry);
255 }
256 }
257
258 /** The messages returned for the service events. */
Richard S. Hallddf2e142009-09-30 17:03:45 +0000259 private static final String[] SERVICE_EVENT_MESSAGES =
260 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000261 "ServiceEvent REGISTERED",
262 "ServiceEvent MODIFIED",
263 "ServiceEvent UNREGISTERING"
264 };
265
266 /**
267 * Called when a service event occurs.
268 * @param event the event that occured
269 */
Richard S. Hallddf2e142009-09-30 17:03:45 +0000270 public void serviceChanged(final ServiceEvent event)
271 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000272 int eventType = event.getType();
273 String message = null;
274
Richard S. Hallddf2e142009-09-30 17:03:45 +0000275 for (int i = 0; message == null && i < SERVICE_EVENT_MESSAGES.length; ++i)
276 {
277 if (eventType >> i == 1)
278 {
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000279 message = SERVICE_EVENT_MESSAGES[i];
280 }
281 }
282
283 LogEntry entry = new LogEntryImpl(event.getServiceReference().getBundle(),
Richard S. Hallddf2e142009-09-30 17:03:45 +0000284 event.getServiceReference(),
285 (eventType == ServiceEvent.MODIFIED) ? LogService.LOG_DEBUG : LogService.LOG_INFO,
286 message,
287 null);
Richard S. Hall0b8e3ba2006-10-25 13:26:32 +0000288
289 addEntry(entry);
290 }
Richard S. Hallddf2e142009-09-30 17:03:45 +0000291}