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