0d0996ba61d511c6677e418540cf045bc8d2f3a7
[public/netxms.git] / webui / webapp / ObjectTools / src / org / netxms / ui / eclipse / objecttools / views / FileViewer.java
1 /**
2 * NetXMS - open source network management system
3 * Copyright (C) 2003-2014 Raden Solutions
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 package org.netxms.ui.eclipse.objecttools.views;
20
21 import java.io.File;
22 import java.io.FileReader;
23 import java.io.IOException;
24 import java.io.UnsupportedEncodingException;
25 import java.net.URLDecoder;
26 import java.util.Arrays;
27 import org.eclipse.core.runtime.IProgressMonitor;
28 import org.eclipse.core.runtime.jobs.Job;
29 import org.eclipse.jface.action.Action;
30 import org.eclipse.jface.action.IMenuListener;
31 import org.eclipse.jface.action.IMenuManager;
32 import org.eclipse.jface.action.IToolBarManager;
33 import org.eclipse.jface.action.MenuManager;
34 import org.eclipse.jface.commands.ActionHandler;
35 import org.eclipse.rap.rwt.RWT;
36 import org.eclipse.swt.SWT;
37 import org.eclipse.swt.graphics.Color;
38 import org.eclipse.swt.widgets.Composite;
39 import org.eclipse.swt.widgets.Display;
40 import org.eclipse.swt.widgets.Menu;
41 import org.eclipse.swt.widgets.Text;
42 import org.eclipse.ui.IActionBars;
43 import org.eclipse.ui.IViewSite;
44 import org.eclipse.ui.PartInitException;
45 import org.eclipse.ui.contexts.IContextService;
46 import org.eclipse.ui.handlers.IHandlerService;
47 import org.eclipse.ui.part.ViewPart;
48 import org.netxms.client.AgentFile;
49 import org.netxms.client.NXCSession;
50 import org.netxms.client.SessionListener;
51 import org.netxms.client.SessionNotification;
52 import org.netxms.client.objects.AbstractObject;
53 import org.netxms.ui.eclipse.console.resources.SharedIcons;
54 import org.netxms.ui.eclipse.jobs.ConsoleJob;
55 import org.netxms.ui.eclipse.objecttools.Activator;
56 import org.netxms.ui.eclipse.objecttools.Messages;
57 import org.netxms.ui.eclipse.shared.ConsoleSharedData;
58
59 /**
60 * File viewer
61 */
62 public class FileViewer extends ViewPart
63 {
64 public static final String ID = "org.netxms.ui.eclipse.objecttools.views.FileViewer"; //$NON-NLS-1$
65
66 private long nodeId;
67 private String remoteFileName;
68 private String fileID;
69 private File currentFile;
70 private Text textViewer;
71 private final NXCSession session = (NXCSession)ConsoleSharedData.getSession();
72 private boolean follow;
73 private ConsoleJob monitorJob;
74 private ConsoleJob tryToRestartMonitoring;
75 private Action actionClear;
76 private Action actionScrollLock;
77 private long offset = 0;
78 private SessionListener listener;
79
80 /* (non-Javadoc)
81 * @see org.eclipse.ui.part.ViewPart#init(org.eclipse.ui.IViewSite)
82 */
83 @Override
84 public void init(IViewSite site) throws PartInitException
85 {
86 super.init(site);
87
88 // Secondary ID must by in form nodeId&remoteFileName
89 String[] parts = site.getSecondaryId().split("&"); //$NON-NLS-1$
90 if (parts.length != 2)
91 throw new PartInitException("Internal error"); //$NON-NLS-1$
92
93 nodeId = Long.parseLong(parts[0]);
94 AbstractObject object = session.findObjectById(nodeId);
95 if ((object == null) || (object.getObjectClass() != AbstractObject.OBJECT_NODE))
96 throw new PartInitException(Messages.get().FileViewer_InvalidObjectID);
97
98 try
99 {
100 remoteFileName = URLDecoder.decode(parts[1], "UTF-8"); //$NON-NLS-1$
101 }
102 catch(UnsupportedEncodingException e)
103 {
104 throw new PartInitException("Internal error", e); //$NON-NLS-1$
105 }
106
107 setPartName(object.getObjectName() + ": " + remoteFileName); //$NON-NLS-1$
108 }
109
110 /* (non-Javadoc)
111 * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
112 */
113 @Override
114 public void createPartControl(Composite parent)
115 {
116 textViewer = new Text(parent, SWT.H_SCROLL | SWT.V_SCROLL);
117 textViewer.setEditable(false);
118 textViewer.setData(RWT.CUSTOM_VARIANT, "monospace");
119
120 createActions();
121 contributeToActionBars();
122 createPopupMenu();
123 activateContext();
124 }
125
126 /**
127 * Activate context
128 */
129 private void activateContext()
130 {
131 IContextService contextService = (IContextService)getSite().getService(IContextService.class);
132 if (contextService != null)
133 {
134 contextService.activateContext("org.netxms.ui.eclipse.objecttools.context.FileViewer"); //$NON-NLS-1$
135 }
136 }
137
138 /**
139 * Create actions
140 */
141 private void createActions()
142 {
143 final IHandlerService handlerService = (IHandlerService)getSite().getService(IHandlerService.class);
144
145 actionClear = new Action(Messages.get().FileViewer_ClearOutput, SharedIcons.CLEAR_LOG) {
146 @Override
147 public void run()
148 {
149 textViewer.setText(""); //$NON-NLS-1$
150 }
151 };
152 actionClear.setActionDefinitionId("org.netxms.ui.eclipse.objecttools.commands.clear_output"); //$NON-NLS-1$
153 handlerService.activateHandler(actionClear.getActionDefinitionId(), new ActionHandler(actionClear));
154
155 actionScrollLock = new Action(Messages.get().FileViewer_ScrollLock, Action.AS_CHECK_BOX) {
156 @Override
157 public void run()
158 {
159 }
160 };
161 actionScrollLock.setImageDescriptor(Activator.getImageDescriptor("icons/scroll_lock.gif")); //$NON-NLS-1$
162 actionScrollLock.setChecked(false);
163 actionScrollLock.setActionDefinitionId("org.netxms.ui.eclipse.objecttools.commands.scroll_lock"); //$NON-NLS-1$
164 handlerService.activateHandler(actionScrollLock.getActionDefinitionId(), new ActionHandler(actionScrollLock));
165 }
166
167 /**
168 * Contribute actions to action bar
169 */
170 private void contributeToActionBars()
171 {
172 IActionBars bars = getViewSite().getActionBars();
173 fillLocalPullDown(bars.getMenuManager());
174 fillLocalToolBar(bars.getToolBarManager());
175 }
176
177 /**
178 * Fill local pull-down menu
179 *
180 * @param manager
181 * Menu manager for pull-down menu
182 */
183 private void fillLocalPullDown(IMenuManager manager)
184 {
185 manager.add(actionClear);
186 manager.add(actionScrollLock);
187 }
188
189 /**
190 * Fill local tool bar
191 *
192 * @param manager
193 * Menu manager for local toolbar
194 */
195 private void fillLocalToolBar(IToolBarManager manager)
196 {
197 manager.add(actionClear);
198 manager.add(actionScrollLock);
199 }
200
201 /**
202 * Create pop-up menu
203 */
204 private void createPopupMenu()
205 {
206 // Create menu manager
207 MenuManager menuMgr = new MenuManager();
208 menuMgr.setRemoveAllWhenShown(true);
209 menuMgr.addMenuListener(new IMenuListener() {
210 public void menuAboutToShow(IMenuManager mgr)
211 {
212 fillContextMenu(mgr);
213 }
214 });
215
216 // Create menu
217 Menu menu = menuMgr.createContextMenu(textViewer);
218 textViewer.setMenu(menu);
219 }
220
221 /**
222 * Fill context menu
223 *
224 * @param mgr Menu manager
225 */
226 private void fillContextMenu(final IMenuManager manager)
227 {
228 manager.add(actionClear);
229 manager.add(actionScrollLock);
230 }
231
232 /* (non-Javadoc)
233 * @see org.eclipse.ui.part.WorkbenchPart#setFocus()
234 */
235 @Override
236 public void setFocus()
237 {
238 textViewer.setFocus();
239 }
240
241 /**
242 * @param file
243 * @param maxFileSize
244 */
245 public void showFile(File file, boolean follow, String id, int maxFileSize)
246 {
247 currentFile = file;
248 fileID = id;
249 offset = maxFileSize;
250 setContent(loadFile(currentFile));
251
252 this.follow = follow;
253 if (follow)
254 {
255 monitorJob = new ConsoleJob(Messages.get().FileViewer_Download_File_Updates, null, Activator.PLUGIN_ID, null) {
256 private boolean continueWork = true;
257
258 @Override
259 protected void canceling()
260 {
261 continueWork = false;
262 }
263
264 @Override
265 protected void runInternal(IProgressMonitor monitor) throws Exception
266 {
267 while(continueWork)
268 {
269 final String s = session.waitForFileTail(fileID, 3000);
270 if (s != null)
271 {
272 runInUIThread(new Runnable() {
273 @Override
274 public void run()
275 {
276 if (!textViewer.isDisposed())
277 {
278 appendContent(s);
279
280 }
281 }
282 });
283 }
284 }
285 }
286
287 @Override
288 protected String getErrorMessage()
289 {
290 return String.format(Messages.get().ObjectToolsDynamicMenu_DownloadError, remoteFileName, nodeId);
291 }
292 };
293 monitorJob.setUser(false);
294 monitorJob.setSystem(true);
295 monitorJob.start();
296
297 listener = new SessionListener() {
298
299 @Override
300 public void notificationHandler(SessionNotification n)
301 {
302 switch(n.getCode())
303 {
304 case SessionNotification.FILE_MONITORING_FAILED:
305 //Check that this is applicable on current file
306 if(nodeId == n.getSubCode())
307 onFileMonitoringFail();
308 break;
309 }
310 }
311 };
312
313 session.addListener(listener);
314 }
315 }
316
317 private void onFileMonitoringFail()
318 {
319 Display d = getSite().getShell().getDisplay();
320 tryToRestartMonitoring = new ConsoleJob(Messages.get(d).FileViewer_RestartFollowingJob, null, Activator.PLUGIN_ID, null, d) {
321 private boolean continueWork = true;
322
323 @Override
324 protected void canceling()
325 {
326 continueWork = false;
327 }
328
329 @Override
330 protected void runInternal(IProgressMonitor monitor) throws Exception
331 {
332 runInUIThread(new Runnable() {
333 @Override
334 public void run()
335 {
336 if (!textViewer.isDisposed())
337 {
338 textViewer.setForeground(new Color(getDisplay(), 255, 0, 0));
339 textViewer.append(" \n\n" + //$NON-NLS-1$
340 "----------------------------------------------------------------------\n" + //$NON-NLS-1$
341 Messages.get().FileViewer_NotifyFollowConnectionLost +
342 "\n----------------------------------------------------------------------" + //$NON-NLS-1$
343 "\n"); //$NON-NLS-1$
344 }
345 }
346 });
347
348 //Try to reconnect in loop every 20 sec.
349 while(continueWork)
350 {
351 try
352 {
353 final AgentFile file = session.downloadFileFromAgent(nodeId, remoteFileName, offset, follow);
354
355 //When successfully connected - display notification to client.
356 runInUIThread(new Runnable() {
357 @Override
358 public void run()
359 {
360 if (!textViewer.isDisposed())
361 {
362 textViewer.append(
363 "-------------------------------------------------------------------------------\n" + //$NON-NLS-1$
364 Messages.get().FileViewer_NotifyFollowConnectionEnabed +
365 "\n-------------------------------------------------------------------------------" + //$NON-NLS-1$
366 "\n \n"); //$NON-NLS-1$
367 textViewer.setForeground(null);
368 loadFile(file.getFile());
369 }
370 }
371 });
372
373 continueWork = false;
374 }
375 catch(Exception e)
376 {
377 }
378 Thread.sleep(20000);
379 }
380
381 }
382
383 @Override
384 protected String getErrorMessage()
385 {
386 return String.format(Messages.get().ObjectToolsDynamicMenu_DownloadError, remoteFileName, nodeId);
387 }
388 };
389 tryToRestartMonitoring.setUser(false);
390 tryToRestartMonitoring.setSystem(true);
391 tryToRestartMonitoring.start();
392 }
393
394 /* (non-Javadoc)
395 * @see org.eclipse.ui.part.WorkbenchPart#dispose()
396 */
397 @Override
398 public void dispose()
399 {
400 if (follow)
401 {
402 monitorJob.cancel();
403 if(tryToRestartMonitoring != null)
404 tryToRestartMonitoring.cancel();
405 if(tryToRestartMonitoring == null || tryToRestartMonitoring.getState() != Job.RUNNING)
406 {
407 final ConsoleJob job = new ConsoleJob(Messages.get().FileViewer_Stop_File_Monitoring, null, Activator.PLUGIN_ID, null) {
408 @Override
409 protected void runInternal(IProgressMonitor monitor) throws Exception
410 {
411 session.cancelFileMonitoring(nodeId, fileID);
412 }
413
414 @Override
415 protected String getErrorMessage()
416 {
417 return Messages.get().FileViewer_Cannot_Stop_File_Monitoring;
418 }
419 };
420 job.setUser(false);
421 job.setSystem(true);
422 job.start();
423 }
424 }
425 session.removeListener(listener);
426 super.dispose();
427 }
428
429 /**
430 * @param file
431 */
432 private String loadFile(File file)
433 {
434 StringBuilder content = new StringBuilder();
435 FileReader reader = null;
436 char[] buffer = new char[32768];
437 try
438 {
439 reader = new FileReader(file);
440 int size = 0;
441 while(size < 8192000)
442 {
443 int count = reader.read(buffer);
444 if (count == -1)
445 break;
446 if (count == buffer.length)
447 {
448 content.append(buffer);
449 }
450 else
451 {
452 content.append(Arrays.copyOf(buffer, count));
453 }
454 size += count;
455 }
456 }
457 catch(IOException e)
458 {
459 e.printStackTrace();
460 }
461 finally
462 {
463 if (reader != null)
464 {
465 try
466 {
467 reader.close();
468 }
469 catch(IOException e)
470 {
471 }
472 }
473 }
474 return content.toString();
475 }
476
477 /**
478 * @param s
479 */
480 private void setContent(String s)
481 {
482 textViewer.setText(removeEscapeSequences(s));
483 }
484
485 /**
486 * @param s
487 */
488 private void appendContent(String s)
489 {
490 textViewer.append(removeEscapeSequences(s));
491 }
492
493 /**
494 * Remove escape sequences from input string
495 *
496 * @param s
497 * @return
498 */
499 private static String removeEscapeSequences(String s)
500 {
501 StringBuilder sb = new StringBuilder();
502 for(int i = 0; i < s.length(); i++)
503 {
504 char ch = s.charAt(i);
505 if (ch == 27)
506 {
507 i++;
508 ch = s.charAt(i);
509 if (ch == '[')
510 {
511 for(; i < s.length(); i++)
512 {
513 ch = s.charAt(i);
514 if (((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z')))
515 break;
516 }
517 }
518 else if ((ch == '(') || (ch == ')'))
519 {
520 i++;
521 }
522 }
523 else if ((ch >= 32) || (ch == '\r') || (ch == '\n') || (ch == '\t'))
524 {
525 sb.append(ch);
526 }
527 }
528 return sb.toString();
529 }
530 }