1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.module.admininterface.pages;
35
36 import info.magnolia.cms.core.Path;
37 import info.magnolia.init.MagnoliaConfigurationProperties;
38 import info.magnolia.module.admininterface.TemplatedMVCHandler;
39 import info.magnolia.objectfactory.Components;
40
41 import java.io.File;
42 import java.io.FileInputStream;
43 import java.io.FileNotFoundException;
44 import java.io.FileReader;
45 import java.io.IOException;
46 import java.io.LineNumberReader;
47 import java.io.PrintWriter;
48 import java.io.StringWriter;
49 import java.util.ArrayList;
50 import java.util.Collection;
51 import java.util.Iterator;
52
53 import javax.servlet.ServletOutputStream;
54 import javax.servlet.http.HttpServletRequest;
55 import javax.servlet.http.HttpServletResponse;
56
57 import org.apache.commons.io.FileUtils;
58 import org.apache.commons.io.IOUtils;
59 import org.apache.commons.io.filefilter.TrueFileFilter;
60 import org.apache.commons.io.filefilter.WildcardFileFilter;
61 import org.apache.commons.lang.StringEscapeUtils;
62 import org.apache.commons.lang.StringUtils;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66 public class LogViewerPage extends TemplatedMVCHandler {
67 private static final Logger log = LoggerFactory.getLogger(LogViewerPage.class);
68
69 public static final String LOGS_FOLDER_PROPERTY = "magnolia.logs.dir";
70
71 public LogViewerPage(String name, HttpServletRequest request, HttpServletResponse response) {
72 super(name, request, response);
73
74
75 MagnoliaConfigurationProperties properties = Components.getComponent(MagnoliaConfigurationProperties.class);
76 String temp = properties.getProperty(LOGS_FOLDER_PROPERTY);
77 if (temp != null) {
78 logsFolder = Path.getAbsoluteFileSystemPath(temp);
79 }
80 }
81
82 private String logsFolder = "";
83
84 private String fileName = "";
85
86 private String text = "";
87
88 private Collection namesList = null;
89
90 private long maxNumLinesPerPage = 50;
91
92 private long pageNumber = 0;
93
94 private long totalPages = 0;
95
96 private long currentPosition = 0;
97
98 private long fileSizeInLines = 0;
99
100
101
102
103
104
105 public Collection getLogFiles() {
106
107 ArrayList urls = new ArrayList();
108
109 File logDir = new File(this.logsFolder);
110 Collection files = null;
111 if (logDir.exists()) {
112 files = FileUtils.listFiles(logDir, new WildcardFileFilter("*.log*"), TrueFileFilter.TRUE);
113
114 Iterator filesIterator = files.iterator();
115 String name = "";
116 while (filesIterator.hasNext()) {
117 name = ((File) filesIterator.next()).getName();
118 urls.add(name);
119 }
120 }
121 return urls;
122 }
123
124 public String refresh() {
125 displayFileContent();
126 return VIEW_SHOW;
127 }
128
129 public String next() {
130 this.currentPosition = Math.min(this.currentPosition + this.maxNumLinesPerPage, this.fileSizeInLines - 1);
131 displayFileContent();
132 return VIEW_SHOW;
133 }
134
135 public String previous() {
136 this.currentPosition = Math.max(0, this.currentPosition - this.maxNumLinesPerPage);
137 displayFileContent();
138 return VIEW_SHOW;
139 }
140
141 public String begin() {
142 this.currentPosition = 0;
143 displayFileContent();
144 return VIEW_SHOW;
145 }
146
147 public String end() {
148 if (this.fileSizeInLines > this.maxNumLinesPerPage) {
149 this.currentPosition = this.fileSizeInLines - (this.fileSizeInLines % this.maxNumLinesPerPage);
150 } else {
151 currentPosition = 0;
152 }
153 displayFileContent();
154 return VIEW_SHOW;
155 }
156
157 public String download() throws FileNotFoundException {
158
159 if (isValidPath()) {
160 File file = getFile();
161 this.getResponse().setContentType("html/text");
162 this.getResponse().setHeader("Content-Disposition", "attachment; filename=" + fileName);
163
164 FileInputStream is = new FileInputStream(file);
165 this.getResponse().setContentLength((int) file.length());
166
167 try {
168 sendUnCompressed(is, this.getResponse());
169 } catch (Exception e) {
170 log.info("File download failed [{}]: {}", fileName, e.getMessage());
171 } finally {
172 IOUtils.closeQuietly(is);
173 }
174 }
175 return "";
176 }
177
178 private void sendUnCompressed(java.io.InputStream is, HttpServletResponse res) throws Exception {
179 ServletOutputStream os = res.getOutputStream();
180 byte[] buffer = new byte[8192];
181 int read = 0;
182 while ((read = is.read(buffer)) > 0) {
183 os.write(buffer, 0, read);
184 }
185 os.flush();
186 os.close();
187 }
188
189 public String displayFileContent() {
190
191 StringWriter str = new StringWriter();
192 if (StringUtils.isNotEmpty(this.fileName) && isValidPath()) {
193 FileReader logFile = null;
194 LineNumberReader input = null;
195 try {
196
197 File file = getFile();
198
199 logFile = new FileReader(file);
200 this.fileSizeInLines = countLines(file);
201 PrintWriter writer = new PrintWriter(str);
202
203 input = new LineNumberReader(logFile);
204
205 String line;
206 int numLines = 0;
207 while ((line = input.readLine()) != null) {
208
209 if (input.getLineNumber() >= this.currentPosition) {
210 writer.write(StringEscapeUtils.escapeHtml(line));
211 writer.write("<br/>");
212 numLines++;
213 }
214 if (numLines >= this.maxNumLinesPerPage) {
215 break;
216 }
217 }
218 writer.flush();
219 this.text = str.toString();
220 writer.close();
221
222 setFieldValues();
223
224 } catch (Exception e) {
225 this.text = e.getMessage();
226 log.error("Error can't read file:", e);
227 return VIEW_SHOW;
228 } finally {
229
230 closeFile(logFile, input);
231 }
232 }
233
234 return VIEW_SHOW;
235 }
236
237
238
239
240 private boolean isValidPath() {
241 String canonicalPath = null;
242 try {
243 File file = getFile();
244 if (file != null) {
245 canonicalPath = file.getCanonicalPath();
246 } else {
247 this.text = "Invalid file path.";
248 return false;
249 }
250 } catch (IOException e) {
251 this.text = e.getMessage();
252 log.error("Can't read file: ", e);
253 return false;
254 }
255 String logFolderPath = null;
256 try {
257 logFolderPath = new File(this.logsFolder).getCanonicalPath();
258 } catch (IOException e) {
259 log.warn("Cannot resolve symlinks for logs folder '{}'. If the property '" + LOGS_FOLDER_PROPERTY +
260 "' contains symlinks, they won't be resolved.", this.logsFolder, e);
261 logFolderPath = this.logsFolder;
262 }
263 if (StringUtils.startsWith(canonicalPath, logFolderPath)) {
264 return true;
265 }
266 this.text = "Invalid file path.";
267 return false;
268 }
269
270 private File getFile() {
271 if (this.fileName.length() > 0) {
272 File file = new File(this.logsFolder + "/" + this.fileName);
273 return file;
274 }
275 return null;
276 }
277
278 private void setFieldValues() {
279 this.pageNumber = (this.currentPosition / this.maxNumLinesPerPage) + 1;
280 this.totalPages = (this.fileSizeInLines / this.maxNumLinesPerPage) + 1;
281 long mod = this.currentPosition % this.maxNumLinesPerPage;
282 if (mod > 0) {
283
284 this.pageNumber++;
285 this.totalPages++;
286 }
287 }
288
289
290 private long countLines(File file) {
291 int count = 0;
292 FileReader fileReader = null;
293 LineNumberReader lineReader = null;
294 try {
295 fileReader = new FileReader(file);
296 lineReader = new LineNumberReader(fileReader);
297
298 lineReader.skip(file.length() );
299 count = lineReader.getLineNumber() + 1;
300
301 } catch (Exception e) {
302 count = 0;
303 } finally {
304
305 closeFile(fileReader, lineReader);
306 }
307
308 return count;
309 }
310
311 private void closeFile(FileReader fileReader, LineNumberReader lineReader) {
312 try {
313 if (fileReader != null) {
314 fileReader.close();
315
316 }
317 if (lineReader != null) {
318 lineReader.close();
319 }
320 } catch (Exception e) {
321
322 }
323 }
324
325 public String getFileName() {
326 return this.fileName;
327 }
328
329 public void setFileName(String fileName) {
330 this.fileName = fileName;
331 }
332
333 public Collection getNamesList() {
334 if (this.namesList == null) {
335 this.namesList = getLogFiles();
336 }
337 return this.namesList;
338 }
339
340 public String getText() {
341 return this.text;
342 }
343
344 public long getCurrentPosition() {
345 return this.currentPosition;
346 }
347
348 public void setCurrentPosition(long currentPosition) {
349
350 this.currentPosition = Math.max(0, Math.min(currentPosition, this.fileSizeInLines - (this.fileSizeInLines % this.maxNumLinesPerPage)));
351 }
352
353 public long getFileSizeInLines() {
354 return this.fileSizeInLines;
355 }
356
357 public void setFileSizeInLines(long fileSizeInLines) {
358 this.fileSizeInLines = fileSizeInLines;
359 }
360
361 public long getPageNumber() {
362 return this.pageNumber;
363 }
364
365 public long getTotalPages() {
366 return this.totalPages;
367 }
368
369 public long getMaxNumLinesPerPage() {
370 return this.maxNumLinesPerPage;
371 }
372
373
374
375
376
377 public void setMaxNumLinesPerPage(long maxNumLinesPerPage) {
378 if (maxNumLinesPerPage < 1) {
379 return;
380 }
381 this.maxNumLinesPerPage = maxNumLinesPerPage;
382 }
383
384 }