3ebbbccf3d81f361a65d17a3acf552f066a9f998
[public/netxms.git] / src / client / java / netxms-client / src / main / java / org / netxms / client / NXCSession.java
1 /**
2 * NetXMS - open source network management system
3 * Copyright (C) 2003-2017 Victor Kirhenshtein
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.client;
20
21 import java.io.BufferedInputStream;
22 import java.io.ByteArrayInputStream;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.io.Writer;
29 import java.net.InetAddress;
30 import java.net.InetSocketAddress;
31 import java.net.Socket;
32 import java.net.UnknownHostException;
33 import java.security.GeneralSecurityException;
34 import java.security.Signature;
35 import java.security.SignatureException;
36 import java.security.cert.Certificate;
37 import java.security.cert.CertificateEncodingException;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.Collection;
41 import java.util.Collections;
42 import java.util.Comparator;
43 import java.util.Date;
44 import java.util.HashMap;
45 import java.util.HashSet;
46 import java.util.Iterator;
47 import java.util.List;
48 import java.util.Locale;
49 import java.util.Map;
50 import java.util.Map.Entry;
51 import java.util.Set;
52 import java.util.UUID;
53 import java.util.concurrent.LinkedBlockingQueue;
54 import java.util.concurrent.Semaphore;
55 import java.util.concurrent.TimeUnit;
56 import java.util.concurrent.atomic.AtomicLong;
57 import java.util.regex.Matcher;
58 import java.util.regex.Pattern;
59 import org.netxms.base.EncryptionContext;
60 import org.netxms.base.GeoLocation;
61 import org.netxms.base.InetAddressEx;
62 import org.netxms.base.Logger;
63 import org.netxms.base.MacAddress;
64 import org.netxms.base.NXCPCodes;
65 import org.netxms.base.NXCPDataInputStream;
66 import org.netxms.base.NXCPException;
67 import org.netxms.base.NXCPMessage;
68 import org.netxms.base.NXCPMessageReceiver;
69 import org.netxms.base.NXCPMsgWaitQueue;
70 import org.netxms.base.NXCommon;
71 import org.netxms.client.agent.config.ConfigContent;
72 import org.netxms.client.agent.config.ConfigListElement;
73 import org.netxms.client.constants.AggregationFunction;
74 import org.netxms.client.constants.AuthenticationType;
75 import org.netxms.client.constants.NodePollType;
76 import org.netxms.client.constants.ObjectStatus;
77 import org.netxms.client.constants.RCC;
78 import org.netxms.client.dashboards.DashboardElement;
79 import org.netxms.client.datacollection.ConditionDciInfo;
80 import org.netxms.client.datacollection.DataCollectionConfiguration;
81 import org.netxms.client.datacollection.DataCollectionItem;
82 import org.netxms.client.datacollection.DciData;
83 import org.netxms.client.datacollection.DciDataRow;
84 import org.netxms.client.datacollection.DciPushData;
85 import org.netxms.client.datacollection.DciSummaryTable;
86 import org.netxms.client.datacollection.DciSummaryTableColumn;
87 import org.netxms.client.datacollection.DciSummaryTableDescriptor;
88 import org.netxms.client.datacollection.DciValue;
89 import org.netxms.client.datacollection.GraphSettings;
90 import org.netxms.client.datacollection.PerfTabDci;
91 import org.netxms.client.datacollection.PredictionEngine;
92 import org.netxms.client.datacollection.SimpleDciValue;
93 import org.netxms.client.datacollection.Threshold;
94 import org.netxms.client.datacollection.ThresholdViolationSummary;
95 import org.netxms.client.datacollection.TransformationTestResult;
96 import org.netxms.client.datacollection.WinPerfObject;
97 import org.netxms.client.events.Alarm;
98 import org.netxms.client.events.AlarmCategory;
99 import org.netxms.client.events.AlarmComment;
100 import org.netxms.client.events.BulkAlarmStateChangeData;
101 import org.netxms.client.events.Event;
102 import org.netxms.client.events.EventInfo;
103 import org.netxms.client.events.EventObject;
104 import org.netxms.client.events.EventProcessingPolicy;
105 import org.netxms.client.events.EventProcessingPolicyRule;
106 import org.netxms.client.events.SyslogRecord;
107 import org.netxms.client.log.Log;
108 import org.netxms.client.maps.MapDCIInstance;
109 import org.netxms.client.maps.NetworkMapLink;
110 import org.netxms.client.maps.NetworkMapPage;
111 import org.netxms.client.maps.configs.SingleDciConfig;
112 import org.netxms.client.maps.elements.NetworkMapElement;
113 import org.netxms.client.maps.elements.NetworkMapObject;
114 import org.netxms.client.market.Repository;
115 import org.netxms.client.mt.MappingTable;
116 import org.netxms.client.mt.MappingTableDescriptor;
117 import org.netxms.client.objects.AbstractNode;
118 import org.netxms.client.objects.AbstractObject;
119 import org.netxms.client.objects.AccessPoint;
120 import org.netxms.client.objects.AgentPolicy;
121 import org.netxms.client.objects.AgentPolicyConfig;
122 import org.netxms.client.objects.AgentPolicyLogParser;
123 import org.netxms.client.objects.BusinessService;
124 import org.netxms.client.objects.BusinessServiceRoot;
125 import org.netxms.client.objects.Chassis;
126 import org.netxms.client.objects.Cluster;
127 import org.netxms.client.objects.ClusterResource;
128 import org.netxms.client.objects.Condition;
129 import org.netxms.client.objects.Container;
130 import org.netxms.client.objects.Dashboard;
131 import org.netxms.client.objects.DashboardGroup;
132 import org.netxms.client.objects.DashboardRoot;
133 import org.netxms.client.objects.EntireNetwork;
134 import org.netxms.client.objects.GenericObject;
135 import org.netxms.client.objects.Interface;
136 import org.netxms.client.objects.MobileDevice;
137 import org.netxms.client.objects.NetworkMap;
138 import org.netxms.client.objects.NetworkMapGroup;
139 import org.netxms.client.objects.NetworkMapRoot;
140 import org.netxms.client.objects.NetworkService;
141 import org.netxms.client.objects.Node;
142 import org.netxms.client.objects.NodeLink;
143 import org.netxms.client.objects.PolicyGroup;
144 import org.netxms.client.objects.PolicyRoot;
145 import org.netxms.client.objects.Rack;
146 import org.netxms.client.objects.Sensor;
147 import org.netxms.client.objects.ServiceCheck;
148 import org.netxms.client.objects.ServiceRoot;
149 import org.netxms.client.objects.Subnet;
150 import org.netxms.client.objects.Template;
151 import org.netxms.client.objects.TemplateGroup;
152 import org.netxms.client.objects.TemplateRoot;
153 import org.netxms.client.objects.UnknownObject;
154 import org.netxms.client.objects.VPNConnector;
155 import org.netxms.client.objects.Zone;
156 import org.netxms.client.objecttools.ObjectContextBase;
157 import org.netxms.client.objecttools.ObjectTool;
158 import org.netxms.client.objecttools.ObjectToolDetails;
159 import org.netxms.client.packages.PackageDeploymentListener;
160 import org.netxms.client.packages.PackageInfo;
161 import org.netxms.client.reporting.ReportDefinition;
162 import org.netxms.client.reporting.ReportRenderFormat;
163 import org.netxms.client.reporting.ReportResult;
164 import org.netxms.client.reporting.ReportingJob;
165 import org.netxms.client.server.AgentFile;
166 import org.netxms.client.server.ServerConsoleListener;
167 import org.netxms.client.server.ServerFile;
168 import org.netxms.client.server.ServerJob;
169 import org.netxms.client.server.ServerJobIdUpdater;
170 import org.netxms.client.server.ServerVariable;
171 import org.netxms.client.snmp.SnmpTrap;
172 import org.netxms.client.snmp.SnmpTrapLogRecord;
173 import org.netxms.client.snmp.SnmpUsmCredential;
174 import org.netxms.client.snmp.SnmpValue;
175 import org.netxms.client.snmp.SnmpWalkListener;
176 import org.netxms.client.topology.ConnectionPoint;
177 import org.netxms.client.topology.FdbEntry;
178 import org.netxms.client.topology.NetworkPath;
179 import org.netxms.client.topology.Route;
180 import org.netxms.client.topology.VlanInfo;
181 import org.netxms.client.topology.WirelessStation;
182 import org.netxms.client.users.AbstractUserObject;
183 import org.netxms.client.users.AuthCertificate;
184 import org.netxms.client.users.User;
185 import org.netxms.client.users.UserGroup;
186 import org.netxms.client.zeromq.ZmqSubscription;
187 import org.netxms.client.zeromq.ZmqSubscriptionType;
188 import com.jcraft.jzlib.Deflater;
189 import com.jcraft.jzlib.JZlib;
190
191 /**
192 * Communication session with NetXMS server.
193 */
194 public class NXCSession
195 {
196 // Various public constants
197 public static final int DEFAULT_CONN_PORT = 4701;
198
199 // Core notification channels
200 public static final String CHANNEL_EVENTS = "Core.Events";
201 public static final String CHANNEL_SYSLOG = "Core.Syslog";
202 public static final String CHANNEL_ALARMS = "Core.Alarms";
203 public static final String CHANNEL_OBJECTS = "Core.Objects";
204 public static final String CHANNEL_SNMP_TRAPS = "Core.SNMP.Traps";
205 public static final String CHANNEL_AUDIT_LOG = "Core.Audit";
206 public static final String CHANNEL_USERDB = "Core.UserDB";
207
208 // Object sync options
209 public static final int OBJECT_SYNC_NOTIFY = 0x0001;
210 public static final int OBJECT_SYNC_WAIT = 0x0002;
211
212 // Configuration import options
213 public static final int CFG_IMPORT_REPLACE_EVENT_BY_CODE = 0x0001;
214 public static final int CFG_IMPORT_REPLACE_EVENT_BY_NAME = 0x0002;
215
216 // Address list IDs
217 public static final int ADDRESS_LIST_DISCOVERY_TARGETS = 1;
218 public static final int ADDRESS_LIST_DISCOVERY_FILTER = 2;
219
220 // Server components
221 public static final int SERVER_COMPONENT_DISCOVERY_MANAGER = 1;
222
223 // Client types
224 public static final int DESKTOP_CLIENT = 0;
225 public static final int WEB_CLIENT = 1;
226 public static final int MOBILE_CLIENT = 2;
227 public static final int TABLET_CLIENT = 3;
228 public static final int APPLICATION_CLIENT = 4;
229
230 // Private constants
231 private static final int CLIENT_CHALLENGE_SIZE = 256;
232 private static final int MAX_DCI_DATA_ROWS = 200000;
233 private static final int MAX_DCI_STRING_VALUE_LENGTH = 256;
234 private static final int RECEIVED_FILE_TTL = 300000; // 300 seconds
235 private static final int FILE_BUFFER_SIZE = 32768; // 32KB
236
237 // Internal synchronization objects
238 private final Semaphore syncObjects = new Semaphore(1);
239 private final Semaphore syncUserDB = new Semaphore(1);
240
241 // Connection-related attributes
242 private String connAddress;
243 private int connPort;
244 private boolean connUseEncryption;
245 private String connClientInfo = "nxjclient/" + NXCommon.VERSION;
246 private int clientType = DESKTOP_CLIENT;
247 private String clientAddress = null;
248 private boolean ignoreProtocolVersion = false;
249 private String clientLanguage = "en";
250
251 // Information about logged in user
252 private int sessionId;
253 private int userId;
254 private String userName;
255 private AuthenticationType authenticationMethod;
256 private long userSystemRights;
257 private boolean passwordExpired;
258 private int graceLogins;
259
260 // Internal communication data
261 private Socket socket = null;
262 private NXCPMsgWaitQueue msgWaitQueue = null;
263 private ReceiverThread recvThread = null;
264 private HousekeeperThread housekeeperThread = null;
265 private AtomicLong requestId = new AtomicLong(1);
266 private boolean connected = false;
267 private boolean disconnected = false;
268 private boolean serverConsoleConnected = false;
269 private boolean allowCompression = false;
270 private EncryptionContext encryptionContext = null;
271
272 // Communication parameters
273 private int defaultRecvBufferSize = 4194304; // Default is 4MB
274 private int maxRecvBufferSize = 33554432; // Max is 32MB
275 private int connectTimeout = 10000; // Default is 10 seconds
276 private int commandTimeout = 30000; // Default is 30 seconds
277 private int serverCommandOutputTimeout = 60000;
278
279 // Notification listeners and queue
280 private LinkedBlockingQueue<SessionNotification> notificationQueue = new LinkedBlockingQueue<SessionNotification>(8192);
281 private Set<SessionListener> listeners = new HashSet<SessionListener>(0);
282 private Set<ServerConsoleListener> consoleListeners = new HashSet<ServerConsoleListener>(0);
283 private Map<Long, ProgressListener> progressListeners = new HashMap<Long, ProgressListener>(0);
284
285 // Message subscriptions
286 private Map<MessageSubscription, MessageHandler> messageSubscriptions = new HashMap<MessageSubscription, MessageHandler>(0);
287
288 // Received files
289 private Map<Long, NXCReceivedFile> receivedFiles = new HashMap<Long, NXCReceivedFile>();
290
291 // Received file updates(for file monitoring)
292 private Map<String, String> recievedUpdates = new HashMap<String, String>();
293
294 // Server information
295 private ProtocolVersion protocolVersion;
296 private String serverVersion = "(unknown)";
297 private long serverId = 0;
298 private String serverTimeZone;
299 private byte[] serverChallenge = new byte[CLIENT_CHALLENGE_SIZE];
300 private boolean zoningEnabled = false;
301 private boolean helpdeskLinkActive = false;
302 private String tileServerURL;
303 private String dateFormat;
304 private String timeFormat;
305 private String shortTimeFormat;
306 private int defaultDciRetentionTime;
307 private int defaultDciPollingInterval;
308 private boolean strictAlarmStatusFlow;
309 private boolean timedAlarmAckEnabled;
310 private int minViewRefreshInterval;
311 private long serverTime = System.currentTimeMillis();
312 private long serverTimeRecvTime = System.currentTimeMillis();
313 private int alarmListDisplayLimit;
314 private Set<String> serverComponents = new HashSet<String>(0);
315 private String serverName;
316 private String serverColor;
317
318 // Objects
319 private Map<Long, AbstractObject> objectList = new HashMap<Long, AbstractObject>();
320 private Map<UUID, AbstractObject> objectListGUID = new HashMap<UUID, AbstractObject>();
321 private Map<Long, Zone> zoneList = new HashMap<Long, Zone>();
322 private boolean objectsSynchronized = false;
323
324 // Users
325 private Map<Long, AbstractUserObject> userDB = new HashMap<Long, AbstractUserObject>();
326
327 // Event objects
328 private Map<Long, EventObject> eventObjects = new HashMap<Long, EventObject>();
329 private boolean eventObjectsNeedSync = false;
330
331 // Alarm categories
332 private Map<Long, AlarmCategory> alarmCategories = new HashMap<Long, AlarmCategory>();
333 private boolean alarmCategoriesNeedSync = false;
334
335 // Message of the day
336 private String messageOfTheDay;
337
338 /**
339 * Message subscription class
340 */
341 final protected class MessageSubscription
342 {
343 protected int messageCode;
344 protected long messageId;
345
346 /**
347 * @param messageCode The message code
348 * @param messageId The message ID
349 */
350 protected MessageSubscription(int messageCode, long messageId)
351 {
352 this.messageCode = messageCode;
353 this.messageId = messageId;
354 }
355
356 /* (non-Javadoc)
357 * @see java.lang.Object#hashCode()
358 */
359 @Override
360 public int hashCode()
361 {
362 final int prime = 31;
363 int result = 1;
364 result = prime * result + messageCode;
365 result = prime * result + (int)(messageId ^ (messageId >>> 32));
366 return result;
367 }
368
369 /* (non-Javadoc)
370 * @see java.lang.Object#equals(java.lang.Object)
371 */
372 @Override
373 public boolean equals(Object obj)
374 {
375 if (this == obj)
376 return true;
377 if (obj == null)
378 return false;
379 if (getClass() != obj.getClass())
380 return false;
381 MessageSubscription other = (MessageSubscription)obj;
382 if (messageCode != other.messageCode)
383 return false;
384 if (messageId != other.messageId)
385 return false;
386 return true;
387 }
388 }
389
390 /**
391 * Receiver thread for NXCSession
392 */
393 private class ReceiverThread extends Thread
394 {
395 ReceiverThread()
396 {
397 setDaemon(true);
398 start();
399 }
400
401 @Override
402 public void run()
403 {
404 final NXCPMessageReceiver receiver = new NXCPMessageReceiver(defaultRecvBufferSize, maxRecvBufferSize);
405 InputStream in;
406
407 try
408 {
409 in = socket.getInputStream();
410 }
411 catch(IOException e)
412 {
413 return; // Stop receiver thread if input stream cannot be obtained
414 }
415
416 while(socket.isConnected())
417 {
418 try
419 {
420 NXCPMessage msg = receiver.receiveMessage(in, encryptionContext);
421 switch(msg.getMessageCode())
422 {
423 case NXCPCodes.CMD_REQUEST_SESSION_KEY:
424 setupEncryption(msg);
425 break;
426 case NXCPCodes.CMD_KEEPALIVE:
427 serverTime = msg.getFieldAsInt64(NXCPCodes.VID_TIMESTAMP) * 1000;
428 serverTimeRecvTime = System.currentTimeMillis();
429 break;
430 case NXCPCodes.CMD_OBJECT:
431 case NXCPCodes.CMD_OBJECT_UPDATE:
432 if (!msg.getFieldAsBoolean(NXCPCodes.VID_IS_DELETED))
433 {
434 final AbstractObject obj = createObjectFromMessage(msg);
435 synchronized(objectList)
436 {
437 objectList.put(obj.getObjectId(), obj);
438 objectListGUID.put(obj.getGuid(), obj);
439 if (obj instanceof Zone)
440 zoneList.put(((Zone)obj).getUIN(), (Zone)obj);
441 }
442 if (msg.getMessageCode() == NXCPCodes.CMD_OBJECT_UPDATE)
443 {
444 sendNotification(new SessionNotification(SessionNotification.OBJECT_CHANGED, obj.getObjectId(), obj));
445 }
446 }
447 else
448 {
449 long objectId = msg.getFieldAsInt32(NXCPCodes.VID_OBJECT_ID);
450 synchronized(objectList)
451 {
452 AbstractObject object = objectList.get(objectId);
453 if (object != null)
454 {
455 objectListGUID.remove(object.getGuid());
456 objectList.remove(objectId);
457 if (object instanceof Zone)
458 zoneList.remove(((Zone)object).getUIN());
459 }
460 }
461 sendNotification(new SessionNotification(SessionNotification.OBJECT_DELETED, objectId));
462 }
463 break;
464 case NXCPCodes.CMD_OBJECT_LIST_END:
465 completeSync(syncObjects);
466 break;
467 case NXCPCodes.CMD_USER_DATA:
468 final User user = new User(msg);
469 synchronized(userDB)
470 {
471 if (user.isDeleted())
472 {
473 userDB.remove(user.getId());
474 }
475 else
476 {
477 userDB.put(user.getId(), user);
478 }
479 }
480 break;
481 case NXCPCodes.CMD_GROUP_DATA:
482 final UserGroup group = new UserGroup(msg);
483 synchronized(userDB)
484 {
485 if (group.isDeleted())
486 {
487 userDB.remove(group.getId());
488 }
489 else
490 {
491 userDB.put(group.getId(), group);
492 }
493 }
494 break;
495 case NXCPCodes.CMD_USER_DB_EOF:
496 completeSync(syncUserDB);
497 break;
498 case NXCPCodes.CMD_USER_DB_UPDATE:
499 processUserDBUpdate(msg);
500 break;
501 case NXCPCodes.CMD_ALARM_UPDATE:
502 sendNotification(new SessionNotification(
503 msg.getFieldAsInt32(NXCPCodes.VID_NOTIFICATION_CODE) + SessionNotification.NOTIFY_BASE,
504 new Alarm(msg)));
505 break;
506 case NXCPCodes.CMD_BULK_ALARM_STATE_CHANGE:
507 processBulkAlarmStateChange(msg);
508 break;
509 case NXCPCodes.CMD_JOB_CHANGE_NOTIFICATION:
510 sendNotification(new SessionNotification(SessionNotification.JOB_CHANGE, new ServerJob(msg)));
511 break;
512 case NXCPCodes.CMD_FILE_DATA:
513 processFileData(msg);
514 break;
515 case NXCPCodes.CMD_FILE_MONITORING:
516 processFileTail(msg);
517 break;
518 case NXCPCodes.CMD_ABORT_FILE_TRANSFER:
519 processFileTransferError(msg);
520 break;
521 case NXCPCodes.CMD_NOTIFY:
522 processNotificationMessage(msg, true);
523 break;
524 case NXCPCodes.CMD_RS_NOTIFY:
525 processNotificationMessage(msg, false);
526 break;
527 case NXCPCodes.CMD_EVENTLOG_RECORDS:
528 processNewEvents(msg);
529 break;
530 case NXCPCodes.CMD_TRAP_LOG_RECORDS:
531 processNewTraps(msg);
532 break;
533 case NXCPCodes.CMD_SYSLOG_RECORDS:
534 processSyslogRecords(msg);
535 break;
536 case NXCPCodes.CMD_ACTION_DB_UPDATE:
537 processActionConfigChange(msg);
538 break;
539 case NXCPCodes.CMD_EVENT_DB_UPDATE:
540 processEventConfigChange(msg);
541 break;
542 case NXCPCodes.CMD_TRAP_CFG_UPDATE:
543 processTrapConfigChange(msg);
544 break;
545 case NXCPCodes.CMD_ADM_MESSAGE:
546 processConsoleOutput(msg);
547 break;
548 case NXCPCodes.CMD_IMAGE_LIBRARY_UPDATE:
549 processImageLibraryUpdate(msg);
550 break;
551 case NXCPCodes.CMD_GRAPH_UPDATE:
552 GraphSettings graph = GraphSettings.createGraphSettings(msg, NXCPCodes.VID_GRAPH_LIST_BASE);
553 sendNotification(new SessionNotification(SessionNotification.PREDEFINED_GRAPHS_CHANGED, graph.getId(), graph));
554 break;
555 case NXCPCodes.CMD_ALARM_CATEGORY_UPDATE:
556 processAlarmCategoryConfigChange(msg);
557 break;
558 default:
559 // Check subscriptions
560 synchronized(messageSubscriptions)
561 {
562 MessageSubscription s = new MessageSubscription(msg.getMessageCode(), msg.getMessageId());
563 MessageHandler handler = messageSubscriptions.get(s);
564 if (handler != null)
565 {
566 if (handler.processMessage(msg))
567 msg = null;
568 if (handler.isComplete())
569 messageSubscriptions.remove(s);
570 else
571 handler.setLastMessageTimestamp(System.currentTimeMillis());
572 }
573 }
574 if (msg != null)
575 {
576 if (msg.getMessageCode() >= 0x1000)
577 {
578 // Custom message
579 sendNotification(new SessionNotification(SessionNotification.CUSTOM_MESSAGE, msg));
580 }
581 msgWaitQueue.putMessage(msg);
582 }
583 break;
584 }
585 }
586 catch(IOException e)
587 {
588 break; // Stop on read errors
589 }
590 catch(NXCPException e)
591 {
592 if (e.getErrorCode() == NXCPException.SESSION_CLOSED) break;
593 }
594 catch(NXCException e)
595 {
596 if (e.getErrorCode() == RCC.ENCRYPTION_ERROR) break;
597 }
598 }
599 }
600
601 /**
602 * Process server console output
603 *
604 * @param msg notification message
605 */
606 private void processConsoleOutput(NXCPMessage msg)
607 {
608 final String text = msg.getFieldAsString(NXCPCodes.VID_MESSAGE);
609 synchronized(consoleListeners)
610 {
611 for(ServerConsoleListener l : consoleListeners)
612 {
613 l.onConsoleOutput(text);
614 }
615 }
616 }
617
618 /**
619 * Process event notification messages.
620 *
621 * @param msg NXCP message
622 */
623 private void processNewEvents(final NXCPMessage msg)
624 {
625 int count = msg.getFieldAsInt32(NXCPCodes.VID_NUM_RECORDS);
626 int order = msg.getFieldAsInt32(NXCPCodes.VID_RECORDS_ORDER);
627 long varId = NXCPCodes.VID_EVENTLOG_MSG_BASE;
628 for(int i = 0; i < count; i++)
629 {
630 Event event = new Event(msg, varId);
631 sendNotification(new SessionNotification(SessionNotification.NEW_EVENTLOG_RECORD, order, event));
632 }
633 }
634
635 /**
636 * Process SNMP trap notification messages.
637 *
638 * @param msg NXCP message
639 */
640 private void processNewTraps(final NXCPMessage msg)
641 {
642 int count = msg.getFieldAsInt32(NXCPCodes.VID_NUM_RECORDS);
643 int order = msg.getFieldAsInt32(NXCPCodes.VID_RECORDS_ORDER);
644 long varId = NXCPCodes.VID_TRAP_LOG_MSG_BASE;
645 for(int i = 0; i < count; i++)
646 {
647 SnmpTrapLogRecord trap = new SnmpTrapLogRecord(msg, varId);
648 sendNotification(new SessionNotification(SessionNotification.NEW_SNMP_TRAP, order, trap));
649 }
650 }
651
652 /**
653 * Process syslog messages.
654 *
655 * @param msg NXCP message
656 */
657 private void processSyslogRecords(final NXCPMessage msg)
658 {
659 int count = msg.getFieldAsInt32(NXCPCodes.VID_NUM_RECORDS);
660 int order = msg.getFieldAsInt32(NXCPCodes.VID_RECORDS_ORDER);
661 long varId = NXCPCodes.VID_SYSLOG_MSG_BASE;
662 for(int i = 0; i < count; i++)
663 {
664 SyslogRecord record = new SyslogRecord(msg, varId);
665 sendNotification(new SessionNotification(SessionNotification.NEW_SYSLOG_RECORD, order, record));
666 }
667 }
668
669 /**
670 * Process CMD_BULK_ALARM_STATE_CHANGE notification message
671 *
672 * @param msg NXCP message
673 */
674 private void processBulkAlarmStateChange(final NXCPMessage msg)
675 {
676 int code = msg.getFieldAsInt32(NXCPCodes.VID_NOTIFICATION_CODE) + SessionNotification.NOTIFY_BASE;
677 sendNotification(new SessionNotification(code, new BulkAlarmStateChangeData(msg)));
678 }
679
680 /**
681 * Process CMD_NOTIFY message
682 *
683 * @param msg NXCP message
684 */
685 private void processNotificationMessage(final NXCPMessage msg, boolean shiftCode)
686 {
687 int code = msg.getFieldAsInt32(NXCPCodes.VID_NOTIFICATION_CODE);
688 if (shiftCode)
689 code += SessionNotification.NOTIFY_BASE;
690 long data = msg.getFieldAsInt64(NXCPCodes.VID_NOTIFICATION_DATA);
691
692 switch(code)
693 {
694 case SessionNotification.ALARM_STATUS_FLOW_CHANGED:
695 strictAlarmStatusFlow = ((int)data != 0);
696 break;
697 case SessionNotification.RELOAD_EVENT_DB:
698 if (eventObjectsNeedSync)
699 {
700 resyncEventObjects();
701 }
702 break;
703 case SessionNotification.SESSION_KILLED:
704 case SessionNotification.SERVER_SHUTDOWN:
705 case SessionNotification.CONNECTION_BROKEN:
706 backgroundDisconnect(code);
707 return; // backgroundDisconnect will send disconnect notification
708 }
709
710 sendNotification(new SessionNotification(code, data));
711 }
712
713 /**
714 * Process CMD_FILE_MONITORING message
715 *
716 * @param msg NXCP message
717 */
718 private void processFileTail(final NXCPMessage msg)
719 {
720 String fileName = msg.getFieldAsString(NXCPCodes.VID_FILE_NAME);
721 String fileContent = msg.getFieldAsString(NXCPCodes.VID_FILE_DATA);
722
723 synchronized(recievedUpdates)
724 {
725 recievedUpdates.put(fileName, fileContent);
726 recievedUpdates.notifyAll();
727 }
728 }
729
730 /**
731 * Process file data
732 *
733 * @param msg
734 */
735 private void processFileData(final NXCPMessage msg)
736 {
737 long id = msg.getMessageId();
738 NXCReceivedFile file;
739 synchronized(receivedFiles)
740 {
741 file = receivedFiles.get(id);
742 if (file == null)
743 {
744 file = new NXCReceivedFile(id);
745 receivedFiles.put(id, file);
746 }
747 }
748 int bytes = file.writeData(msg.getBinaryData(), msg.isCompressedStream());
749 notifyProgressListener(id, bytes);
750 if (msg.isEndOfFile())
751 {
752 file.close();
753 synchronized(receivedFiles)
754 {
755 receivedFiles.notifyAll();
756 }
757 }
758 }
759
760 /**
761 * Process file transfer error
762 *
763 * @param msg
764 */
765 private void processFileTransferError(final NXCPMessage msg)
766 {
767 long id = msg.getMessageId();
768 NXCReceivedFile file;
769 synchronized(receivedFiles)
770 {
771 file = receivedFiles.get(id);
772 if (file == null)
773 {
774 file = new NXCReceivedFile(id);
775 receivedFiles.put(id, file);
776 }
777 file.abortTransfer();
778 receivedFiles.notifyAll();
779 }
780 }
781
782 /**
783 * Process updates in user database
784 *
785 * @param msg Notification message
786 */
787 private void processUserDBUpdate(final NXCPMessage msg)
788 {
789 final int code = msg.getFieldAsInt32(NXCPCodes.VID_UPDATE_TYPE);
790 final long id = msg.getFieldAsInt64(NXCPCodes.VID_USER_ID);
791
792 AbstractUserObject object = null;
793 switch(code)
794 {
795 case SessionNotification.USER_DB_OBJECT_CREATED:
796 case SessionNotification.USER_DB_OBJECT_MODIFIED:
797 object = ((id & 0x80000000) != 0) ? new UserGroup(msg) : new User(msg);
798 synchronized(userDB)
799 {
800 userDB.put(id, object);
801 }
802 break;
803 case SessionNotification.USER_DB_OBJECT_DELETED:
804 synchronized(userDB)
805 {
806 object = userDB.get(id);
807 if (object != null)
808 {
809 userDB.remove(id);
810 }
811 }
812 break;
813 }
814
815 // Send notification if changed object was found in local database copy
816 // or added to it and notification code was known
817 if (object != null)
818 sendNotification(new SessionNotification(SessionNotification.USER_DB_CHANGED, code, object));
819 }
820
821 /**
822 * Process server notification on SNMP trap configuration change
823 *
824 * @param msg notification message
825 */
826 private void processTrapConfigChange(final NXCPMessage msg)
827 {
828 int code = msg.getFieldAsInt32(NXCPCodes.VID_NOTIFICATION_CODE) + SessionNotification.NOTIFY_BASE;
829 long id = msg.getFieldAsInt64(NXCPCodes.VID_TRAP_ID);
830 SnmpTrap trap = (code != SessionNotification.TRAP_CONFIGURATION_DELETED) ? new SnmpTrap(msg) : null;
831 sendNotification(new SessionNotification(code, id, trap));
832 }
833
834 /**
835 * Process server notification on action configuration change
836 *
837 * @param msg notification message
838 */
839 private void processActionConfigChange(final NXCPMessage msg)
840 {
841 int code = msg.getFieldAsInt32(NXCPCodes.VID_NOTIFICATION_CODE) + SessionNotification.NOTIFY_BASE;
842 long id = msg.getFieldAsInt64(NXCPCodes.VID_ACTION_ID);
843 ServerAction action = (code != SessionNotification.ACTION_DELETED) ? new ServerAction(msg) : null;
844 sendNotification(new SessionNotification(code, id, action));
845 }
846
847 /**
848 * Process server notification on event configuration change
849 *
850 * @param msg notification message
851 */
852 private void processEventConfigChange(final NXCPMessage msg)
853 {
854 int code = msg.getFieldAsInt32(NXCPCodes.VID_NOTIFICATION_CODE) + SessionNotification.NOTIFY_BASE;
855 long eventCode = msg.getFieldAsInt64(NXCPCodes.VID_EVENT_CODE);
856 EventObject obj = (code != SessionNotification.EVENT_TEMPLATE_DELETED) ? EventObject.createFromMessage(msg, NXCPCodes.VID_ELEMENT_LIST_BASE) : null;
857 if (eventObjectsNeedSync)
858 {
859 synchronized(eventObjects)
860 {
861 if (code == SessionNotification.EVENT_TEMPLATE_DELETED)
862 {
863 eventObjects.remove(eventCode);
864 }
865 else
866 {
867 eventObjects.put(obj.getCode(), obj);
868 }
869 }
870 }
871 sendNotification(new SessionNotification(code, obj == null ? eventCode : obj.getCode(), obj));
872 }
873
874 /**
875 * Process server notification on image library change
876 *
877 * @param msg notification message
878 */
879 public void processImageLibraryUpdate(NXCPMessage msg)
880 {
881 final UUID imageGuid = msg.getFieldAsUUID(NXCPCodes.VID_GUID);
882 final int flags = msg.getFieldAsInt32(NXCPCodes.VID_FLAGS);
883 sendNotification(new SessionNotification(SessionNotification.IMAGE_LIBRARY_CHANGED,
884 flags == 0 ? SessionNotification.IMAGE_UPDATED : SessionNotification.IMAGE_DELETED, imageGuid));
885 }
886
887 /**
888 * Process server notification on alarm category configuration change
889 *
890 * @param msg notification message
891 */
892 private void processAlarmCategoryConfigChange(final NXCPMessage msg)
893 {
894 int code = msg.getFieldAsInt32(NXCPCodes.VID_NOTIFICATION_CODE) + SessionNotification.NOTIFY_BASE;
895 long categoryId = msg.getFieldAsInt64(NXCPCodes.VID_ELEMENT_LIST_BASE);
896 AlarmCategory ac = (code != SessionNotification.ALARM_CATEGORY_DELETED) ? new AlarmCategory(msg, NXCPCodes.VID_ELEMENT_LIST_BASE) : null;
897 if (alarmCategoriesNeedSync)
898 {
899 synchronized(alarmCategories)
900 {
901 if (code == SessionNotification.ALARM_CATEGORY_DELETED)
902 {
903 alarmCategories.remove(categoryId);
904 }
905 else
906 {
907 alarmCategories.put(categoryId, ac);
908 }
909 }
910 }
911 sendNotification(new SessionNotification(code, categoryId, ac));
912 }
913 }
914
915 /**
916 * Housekeeper thread - cleans received files, etc.
917 *
918 * @author Victor
919 */
920 private class HousekeeperThread extends Thread
921 {
922 private boolean stopFlag = false;
923
924 HousekeeperThread()
925 {
926 setDaemon(true);
927 start();
928 }
929
930 /*
931 * (non-Javadoc)
932 *
933 * @see java.lang.Thread#run()
934 */
935 @Override
936 public void run()
937 {
938 while(!stopFlag)
939 {
940 try
941 {
942 sleep(1000);
943 }
944 catch(InterruptedException e)
945 {
946 }
947
948 long currTime = System.currentTimeMillis();
949
950 // Check for old entries in received files
951 synchronized(receivedFiles)
952 {
953 Iterator<NXCReceivedFile> it = receivedFiles.values().iterator();
954 while(it.hasNext())
955 {
956 NXCReceivedFile file = it.next();
957 if (file.getTimestamp() + RECEIVED_FILE_TTL < currTime)
958 {
959 file.getFile().delete();
960 it.remove();
961 }
962 }
963 }
964
965 // Check timeouts on message subscriptions
966 synchronized(messageSubscriptions)
967 {
968 Iterator<Entry<MessageSubscription, MessageHandler>> it = messageSubscriptions.entrySet().iterator();
969 while(it.hasNext())
970 {
971 Entry<MessageSubscription, MessageHandler> e = it.next();
972 MessageHandler h = e.getValue();
973 if (currTime - h.getLastMessageTimestamp() > h.getMessageWaitTimeout())
974 {
975 h.setTimeout();
976 h.setComplete();
977 it.remove();
978 }
979 }
980 }
981 }
982 }
983
984 /**
985 * @param stopFlag the stopFlag to set
986 */
987 public void setStopFlag(boolean stopFlag)
988 {
989 this.stopFlag = stopFlag;
990 }
991 }
992
993 /**
994 * Notification processor
995 */
996 private class NotificationProcessor extends Thread
997 {
998 private SessionListener[] cachedListenerList = new SessionListener[0];
999
1000 NotificationProcessor()
1001 {
1002 setName("Session Notification Processor");
1003 setDaemon(true);
1004 start();
1005 }
1006
1007 /* (non-Javadoc)
1008 * @see java.lang.Thread#run()
1009 */
1010 @Override
1011 public void run()
1012 {
1013 while(true)
1014 {
1015 SessionNotification n;
1016 try
1017 {
1018 n = notificationQueue.take();
1019 }
1020 catch(InterruptedException e)
1021 {
1022 continue;
1023 }
1024
1025 if (n.getCode() == SessionNotification.STOP_PROCESSING_THREAD)
1026 break;
1027
1028 if (n.getCode() == SessionNotification.UPDATE_LISTENER_LIST)
1029 {
1030 synchronized(listeners)
1031 {
1032 cachedListenerList = listeners.toArray(new SessionListener[listeners.size()]);
1033 }
1034 continue;
1035 }
1036
1037 // loop must be on listeners set copy to prevent
1038 // possible deadlock when one of the listeners calls
1039 // syncExec on UI thread while UI thread trying to add
1040 // new listener and stays locked inside addListener
1041 for(SessionListener l : cachedListenerList)
1042 {
1043 try
1044 {
1045 l.notificationHandler(n);
1046 }
1047 catch(Exception e)
1048 {
1049 Logger.error("NXCSession.NotificationProcessor", "Unhandled exception in notification handler", e);
1050 }
1051 }
1052 }
1053 cachedListenerList = null;
1054 }
1055 }
1056
1057 public NXCSession(String connAddress)
1058 {
1059 this(connAddress, DEFAULT_CONN_PORT, false);
1060 }
1061
1062 public NXCSession(String connAddress, int connPort)
1063 {
1064 this(connAddress, connPort, false);
1065 }
1066
1067 public NXCSession(String connAddress, int connPort, boolean connUseEncryption)
1068 {
1069 this.connAddress = connAddress;
1070 this.connPort = connPort;
1071 this.connUseEncryption = connUseEncryption;
1072 }
1073
1074 /* (non-Javadoc)
1075 * @see java.lang.Object#finalize()
1076 */
1077 @Override
1078 protected void finalize() throws Throwable
1079 {
1080 try
1081 {
1082 disconnect(SessionNotification.CONNECTION_BROKEN);
1083 }
1084 finally
1085 {
1086 super.finalize();
1087 }
1088 }
1089
1090 /**
1091 * Create custom object from NXCP message. May be overridden by derived classes to create custom
1092 * NetXMS objects. This method called before standard object creation, so it can be used for
1093 * overriding standard object classes. If this method returns null, standard object
1094 * creation mechanism will be used. Default implementation will always return null.
1095 *
1096 * @param objectClass NetXMS object class ID
1097 * @param msg Source NXCP message
1098 * @return NetXMS object or null if object cannot be created
1099 */
1100 protected AbstractObject createCustomObjectFromMessage(int objectClass, NXCPMessage msg)
1101 {
1102 return null;
1103 }
1104
1105 /**
1106 * Create object from message
1107 *
1108 * @param msg Source NXCP message
1109 * @return NetXMS object
1110 */
1111 private AbstractObject createObjectFromMessage(NXCPMessage msg)
1112 {
1113 final int objectClass = msg.getFieldAsInt32(NXCPCodes.VID_OBJECT_CLASS);
1114
1115 AbstractObject object = createCustomObjectFromMessage(objectClass, msg);
1116 if (object != null)
1117 return object;
1118
1119 switch(objectClass)
1120 {
1121 case AbstractObject.OBJECT_ACCESSPOINT:
1122 object = new AccessPoint(msg, this);
1123 break;
1124 case AbstractObject.OBJECT_AGENTPOLICY:
1125 object = new AgentPolicy(msg, this);
1126 break;
1127 case AbstractObject.OBJECT_AGENTPOLICY_CONFIG:
1128 object = new AgentPolicyConfig(msg, this);
1129 break;
1130 case AbstractObject.OBJECT_AGENTPOLICY_LOGPARSER:
1131 object = new AgentPolicyLogParser(msg, this);
1132 break;
1133 case AbstractObject.OBJECT_BUSINESSSERVICE:
1134 object = new BusinessService(msg, this);
1135 break;
1136 case AbstractObject.OBJECT_BUSINESSSERVICEROOT:
1137 object = new BusinessServiceRoot(msg, this);
1138 break;
1139 case AbstractObject.OBJECT_CHASSIS:
1140 object = new Chassis(msg, this);
1141 break;
1142 case AbstractObject.OBJECT_CLUSTER:
1143 object = new Cluster(msg, this);
1144 break;
1145 case AbstractObject.OBJECT_CONDITION:
1146 object = new Condition(msg, this);
1147 break;
1148 case AbstractObject.OBJECT_CONTAINER:
1149 object = new Container(msg, this);
1150 break;
1151 case AbstractObject.OBJECT_DASHBOARDGROUP:
1152 object = new DashboardGroup(msg, this);
1153 break;
1154 case AbstractObject.OBJECT_DASHBOARD:
1155 object = new Dashboard(msg, this);
1156 break;
1157 case AbstractObject.OBJECT_DASHBOARDROOT:
1158 object = new DashboardRoot(msg, this);
1159 break;
1160 case AbstractObject.OBJECT_INTERFACE:
1161 object = new Interface(msg, this);
1162 break;
1163 case AbstractObject.OBJECT_MOBILEDEVICE:
1164 object = new MobileDevice(msg, this);
1165 break;
1166 case AbstractObject.OBJECT_NETWORK:
1167 object = new EntireNetwork(msg, this);
1168 break;
1169 case AbstractObject.OBJECT_NETWORKMAP:
1170 object = new NetworkMap(msg, this);
1171 break;
1172 case AbstractObject.OBJECT_NETWORKMAPGROUP:
1173 object = new NetworkMapGroup(msg, this);
1174 break;
1175 case AbstractObject.OBJECT_NETWORKMAPROOT:
1176 object = new NetworkMapRoot(msg, this);
1177 break;
1178 case AbstractObject.OBJECT_NETWORKSERVICE:
1179 object = new NetworkService(msg, this);
1180 break;
1181 case AbstractObject.OBJECT_NODE:
1182 object = new Node(msg, this);
1183 break;
1184 case AbstractObject.OBJECT_NODELINK:
1185 object = new NodeLink(msg, this);
1186 break;
1187 case AbstractObject.OBJECT_POLICYGROUP:
1188 object = new PolicyGroup(msg, this);
1189 break;
1190 case AbstractObject.OBJECT_POLICYROOT:
1191 object = new PolicyRoot(msg, this);
1192 break;
1193 case AbstractObject.OBJECT_RACK:
1194 object = new Rack(msg, this);
1195 break;
1196 case AbstractObject.OBJECT_SENSOR:
1197 object = new Sensor(msg, this);
1198 break;
1199 case AbstractObject.OBJECT_SERVICEROOT:
1200 object = new ServiceRoot(msg, this);
1201 break;
1202 case AbstractObject.OBJECT_SLMCHECK:
1203 object = new ServiceCheck(msg, this);
1204 break;
1205 case AbstractObject.OBJECT_SUBNET:
1206 object = new Subnet(msg, this);
1207 break;
1208 case AbstractObject.OBJECT_TEMPLATE:
1209 object = new Template(msg, this);
1210 break;
1211 case AbstractObject.OBJECT_TEMPLATEGROUP:
1212 object = new TemplateGroup(msg, this);
1213 break;
1214 case AbstractObject.OBJECT_TEMPLATEROOT:
1215 object = new TemplateRoot(msg, this);
1216 break;
1217 case AbstractObject.OBJECT_VPNCONNECTOR:
1218 object = new VPNConnector(msg, this);
1219 break;
1220 case AbstractObject.OBJECT_ZONE:
1221 object = new Zone(msg, this);
1222 break;
1223 default:
1224 object = new GenericObject(msg, this);
1225 break;
1226 }
1227
1228 return object;
1229 }
1230
1231 /**
1232 * Setup encryption
1233 *
1234 * @param msg CMD_REQUEST_SESSION_KEY message
1235 * @throws IOException
1236 * @throws NXCException
1237 */
1238 private void setupEncryption(NXCPMessage msg) throws IOException, NXCException
1239 {
1240 final NXCPMessage response = new NXCPMessage(NXCPCodes.CMD_SESSION_KEY, msg.getMessageId());
1241 response.setEncryptionDisabled(true);
1242
1243 try
1244 {
1245 encryptionContext = EncryptionContext.createInstance(msg);
1246 response.setField(NXCPCodes.VID_SESSION_KEY, encryptionContext.getEncryptedSessionKey(msg));
1247 response.setField(NXCPCodes.VID_SESSION_IV, encryptionContext.getEncryptedIv(msg));
1248 response.setFieldInt16(NXCPCodes.VID_CIPHER, encryptionContext.getCipher());
1249 response.setFieldInt16(NXCPCodes.VID_KEY_LENGTH, encryptionContext.getKeyLength());
1250 response.setFieldInt16(NXCPCodes.VID_IV_LENGTH, encryptionContext.getIvLength());
1251 response.setFieldInt32(NXCPCodes.VID_RCC, RCC.SUCCESS);
1252 Logger.debug("NXCSession.setupEncryption", "Cipher selected: " + EncryptionContext.getCipherName(encryptionContext.getCipher()));
1253 }
1254 catch(Exception e)
1255 {
1256 response.setFieldInt32(NXCPCodes.VID_RCC, RCC.NO_CIPHERS);
1257 }
1258
1259 sendMessage(response);
1260 }
1261
1262 /**
1263 * Wait for synchronization completion
1264 */
1265 private void waitForSync(final Semaphore syncObject, final int timeout) throws NXCException
1266 {
1267 if (timeout == 0)
1268 {
1269 syncObject.acquireUninterruptibly();
1270 }
1271 else
1272 {
1273 long actualTimeout = timeout;
1274 boolean success = false;
1275
1276 while(actualTimeout > 0)
1277 {
1278 long startTime = System.currentTimeMillis();
1279 try
1280 {
1281 if (syncObjects.tryAcquire(actualTimeout, TimeUnit.MILLISECONDS))
1282 {
1283 success = true;
1284 syncObjects.release();
1285 break;
1286 }
1287 }
1288 catch(InterruptedException e)
1289 {
1290 }
1291 actualTimeout -= System.currentTimeMillis() - startTime;
1292 }
1293 if (!success) throw new NXCException(RCC.TIMEOUT);
1294 }
1295 }
1296
1297 /**
1298 * Report synchronization completion
1299 *
1300 * @param syncObject Synchronization object
1301 */
1302 private void completeSync(final Semaphore syncObject)
1303 {
1304 syncObject.release();
1305 }
1306
1307 /**
1308 * Add notification listener
1309 *
1310 * @param listener Listener to add
1311 */
1312 public void addListener(SessionListener listener)
1313 {
1314 boolean changed;
1315 synchronized(listeners)
1316 {
1317 changed = listeners.add(listener);
1318 }
1319 if (changed)
1320 notificationQueue.offer(new SessionNotification(SessionNotification.UPDATE_LISTENER_LIST));
1321 }
1322
1323 /**
1324 * Remove notification listener
1325 *
1326 * @param listener Listener to remove
1327 */
1328 public void removeListener(SessionListener listener)
1329 {
1330 boolean changed;
1331 synchronized(listeners)
1332 {
1333 changed = listeners.remove(listener);
1334 }
1335 if (changed)
1336 notificationQueue.offer(new SessionNotification(SessionNotification.UPDATE_LISTENER_LIST));
1337 }
1338
1339 /**
1340 * Add server console listener
1341 *
1342 * @param listener The ServerConsoleListener to add
1343 */
1344 public void addConsoleListener(ServerConsoleListener listener)
1345 {
1346 synchronized(consoleListeners)
1347 {
1348 consoleListeners.add(listener);
1349 }
1350 }
1351
1352 /**
1353 * Remove server console listener
1354 *
1355 * @param listener The ServerConsoleListener to remove
1356 */
1357 public void removeConsoleListener(ServerConsoleListener listener)
1358 {
1359 synchronized(consoleListeners)
1360 {
1361 consoleListeners.remove(listener);
1362 }
1363 }
1364
1365 /**
1366 * Subscribe to specific messages
1367 *
1368 * @param messageCode The message code
1369 * @param messageId The message ID
1370 * @param handler The message handler
1371 */
1372 public void addMessageSubscription(int messageCode, long messageId, MessageHandler handler)
1373 {
1374 synchronized(messageSubscriptions)
1375 {
1376 messageSubscriptions.put(new MessageSubscription(messageCode, messageId), handler);
1377 }
1378 }
1379
1380 /**
1381 * Remove message subscription
1382 *
1383 * @param messageCode The message code
1384 * @param messageId The message ID
1385 */
1386 public void removeMessageSubscription(int messageCode, long messageId)
1387 {
1388 synchronized(messageSubscriptions)
1389 {
1390 messageSubscriptions.remove(new MessageSubscription(messageCode, messageId));
1391 }
1392 }
1393
1394 /**
1395 * Call notification handlers on all registered listeners
1396 *
1397 * @param n Notification object
1398 */
1399 protected void sendNotification(SessionNotification n)
1400 {
1401 if (!notificationQueue.offer(n))
1402 {
1403 Logger.debug("NXCSession.sendNotification", "Notification processing queue is full");
1404 }
1405 }
1406
1407 /**
1408 * Send message to server
1409 *
1410 * @param msg Message to sent
1411 * @throws IOException in case of socket communication failure
1412 * @throws NXCException in case of encryption error
1413 */
1414 public synchronized void sendMessage(final NXCPMessage msg) throws IOException, NXCException
1415 {
1416 if (socket == null)
1417 {
1418 throw new IllegalStateException("Not connected to the server. Did you forgot to call connect() first?");
1419 }
1420 final OutputStream outputStream = socket.getOutputStream();
1421 byte[] message;
1422 if ((encryptionContext != null) && !msg.isEncryptionDisabled())
1423 {
1424 try
1425 {
1426 message = encryptionContext.encryptMessage(msg, allowCompression);
1427 }
1428 catch(GeneralSecurityException e)
1429 {
1430 throw new NXCException(RCC.ENCRYPTION_ERROR);
1431 }
1432 }
1433 else
1434 {
1435 message = msg.createNXCPMessage(allowCompression);
1436 }
1437 outputStream.write(message);
1438 }
1439
1440 /**
1441 * Send file over NXCP
1442 *
1443 * @param requestId request ID
1444 * @param file source file to be sent
1445 * @param listener progress listener
1446 * @param allowStreamCompression true if data stream compression is allowed
1447 * @throws IOException if socket I/O error occurs
1448 * @throws NXCException if NetXMS server returns an error or operation was timed out
1449 */
1450 protected void sendFile(final long requestId, final File file, ProgressListener listener, boolean allowStreamCompression) throws IOException, NXCException
1451 {
1452 if (listener != null)
1453 listener.setTotalWorkAmount(file.length());
1454 final InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
1455 sendFileStream(requestId, inputStream, listener, allowStreamCompression);
1456 inputStream.close();
1457 }
1458
1459 /**
1460 * Send block of data as binary message
1461 *
1462 * @param requestId request ID
1463 * @param data file data
1464 * @param listener progress listener
1465 * @param allowStreamCompression true if data stream compression is allowed
1466 * @throws IOException if socket I/O error occurs
1467 * @throws NXCException if NetXMS server returns an error or operation was timed out
1468 */
1469 protected void sendFile(final long requestId, final byte[] data, ProgressListener listener, boolean allowStreamCompression) throws IOException, NXCException
1470 {
1471 if (listener != null)
1472 listener.setTotalWorkAmount(data.length);
1473 final InputStream inputStream = new ByteArrayInputStream(data);
1474 sendFileStream(requestId, inputStream, listener, allowStreamCompression);
1475 inputStream.close();
1476 }
1477
1478 /**
1479 * Send binary message, data loaded from provided input stream and splitted
1480 * into chunks of {@value FILE_BUFFER_SIZE} bytes
1481 *
1482 * @param requestId request ID
1483 * @param inputStream data input stream
1484 * @param listener progress listener
1485 * @param allowStreamCompression true if data stream compression is allowed
1486 * @throws IOException if socket I/O error occurs
1487 * @throws NXCException if NetXMS server returns an error or operation was timed out
1488 */
1489 private void sendFileStream(final long requestId, final InputStream inputStream, ProgressListener listener, boolean allowStreamCompression) throws IOException, NXCException
1490 {
1491 NXCPMessage msg = new NXCPMessage(NXCPCodes.CMD_FILE_DATA, requestId);
1492 msg.setBinaryMessage(true);
1493
1494 Deflater compressor = allowStreamCompression ? new Deflater(9) : null;
1495 msg.setStream(true, allowStreamCompression);
1496
1497 boolean success = false;
1498 final byte[] buffer = new byte[FILE_BUFFER_SIZE];
1499 long bytesSent = 0;
1500 while(true)
1501 {
1502 final int bytesRead = inputStream.read(buffer);
1503 if (bytesRead < FILE_BUFFER_SIZE)
1504 {
1505 msg.setEndOfFile(true);
1506 }
1507
1508 if ((compressor != null) && (bytesRead > 0))
1509 {
1510 byte[] compressedData = new byte[compressor.deflateBound(bytesRead) + 4];
1511 compressor.setInput(buffer, 0, bytesRead, false);
1512 compressor.setOutput(compressedData, 4, compressedData.length - 4);
1513 if (compressor.deflate(JZlib.Z_SYNC_FLUSH) != JZlib.Z_OK)
1514 throw new NXCException(RCC.IO_ERROR);
1515 int length = compressedData.length - compressor.getAvailOut();
1516 byte[] payload = Arrays.copyOf(compressedData, length);
1517 payload[0] = 2; // DEFLATE method
1518 payload[1] = 0; // reserved
1519 payload[2] = (byte)((bytesRead >> 8) & 0xFF); // uncompressed length, high bits
1520 payload[3] = (byte)(bytesRead & 0xFF); // uncompressed length, low bits
1521 msg.setBinaryData(payload);
1522 }
1523 else
1524 {
1525 msg.setBinaryData((bytesRead == -1) ? new byte[0] : Arrays.copyOf(buffer, bytesRead));
1526 }
1527 sendMessage(msg);
1528
1529 bytesSent += (bytesRead == -1) ? 0 : bytesRead;
1530 if (listener != null)
1531 listener.markProgress(bytesSent);
1532
1533 if (bytesRead < FILE_BUFFER_SIZE)
1534 {
1535 success = true;
1536 break;
1537 }
1538 }
1539 if (compressor != null)
1540 compressor.deflateEnd();
1541
1542 if (!success)
1543 {
1544 NXCPMessage abortMessage = new NXCPMessage(NXCPCodes.CMD_ABORT_FILE_TRANSFER, requestId);
1545 abortMessage.setBinaryMessage(true);
1546 sendMessage(abortMessage);
1547 waitForRCC(abortMessage.getMessageId());
1548 }
1549 }
1550
1551 /**
1552 * Wait for message with specific code and id.
1553 *
1554 * @param code
1555 * Message code
1556 * @param id
1557 * Message id
1558 * @param timeout
1559 * Wait timeout in milliseconds
1560 * @return Message object
1561 * @throws NXCException
1562 * if message was not arrived within timeout interval
1563 */
1564 public NXCPMessage waitForMessage(final int code, final long id, final int timeout) throws NXCException
1565 {
1566 final NXCPMessage msg = msgWaitQueue.waitForMessage(code, id, timeout);
1567 if (msg == null)
1568 throw new NXCException(RCC.TIMEOUT);
1569 return msg;
1570 }
1571
1572 /**
1573 * Wait for message with specific code and id.
1574 *
1575 * @param code
1576 * Message code
1577 * @param id
1578 * Message id
1579 * @return Message object
1580 * @throws NXCException
1581 * if message was not arrived within timeout interval
1582 */
1583 public NXCPMessage waitForMessage(final int code, final long id) throws NXCException
1584 {
1585 final NXCPMessage msg = msgWaitQueue.waitForMessage(code, id);
1586 if (msg == null)
1587 throw new NXCException(RCC.TIMEOUT);
1588 return msg;
1589 }
1590
1591 /**
1592 * Wait for CMD_REQUEST_COMPLETED message with given id using default timeout
1593 *
1594 * @param id
1595 * Message id
1596 * @return received message
1597 * @throws NXCException
1598 * if message was not arrived within timeout interval or contains RCC other than RCC.SUCCESS
1599 */
1600 public NXCPMessage waitForRCC(final long id) throws NXCException
1601 {
1602 return waitForRCC(id, msgWaitQueue.getDefaultTimeout());
1603 }
1604
1605 /**
1606 * Wait for CMD_REQUEST_COMPLETED message with given id
1607 *
1608 * @param id
1609 * Message id
1610 * @param timeout Timeout in milliseconds
1611 * @return received message
1612 * @throws NXCException
1613 * if message was not arrived within timeout interval or contains RCC other than RCC.SUCCESS
1614 */
1615 public NXCPMessage waitForRCC(final long id, final int timeout) throws NXCException
1616 {
1617 final NXCPMessage msg = waitForMessage(NXCPCodes.CMD_REQUEST_COMPLETED, id, timeout);
1618 final int rcc = msg.getFieldAsInt32(NXCPCodes.VID_RCC);
1619 if (rcc != RCC.SUCCESS)
1620 {
1621 if ((rcc == RCC.COMPONENT_LOCKED) && (msg.findField(NXCPCodes.VID_LOCKED_BY) != null))
1622 {
1623 throw new NXCException(rcc, msg.getFieldAsString(NXCPCodes.VID_LOCKED_BY));
1624 }
1625 else if (msg.findField(NXCPCodes.VID_ERROR_TEXT) != null)
1626 {
1627 throw new NXCException(rcc, msg.getFieldAsString(NXCPCodes.VID_ERROR_TEXT));
1628 }
1629 else if (msg.findField(NXCPCodes.VID_VALUE) != null)
1630 {
1631 throw new NXCException(rcc, msg.getFieldAsString(NXCPCodes.VID_VALUE));
1632 }
1633 else
1634 {
1635 throw new NXCException(rcc);
1636 }
1637 }
1638 return msg;
1639 }
1640
1641 /**
1642 * Create new NXCP message with unique id
1643 *
1644 * @param code
1645 * Message code
1646 * @return New message object
1647 */
1648 public final NXCPMessage newMessage(int code)
1649 {
1650 return new NXCPMessage(code, requestId.getAndIncrement());
1651 }
1652
1653 /**
1654 * Wait for specific file to arrive
1655 *
1656 * @param id Message ID
1657 * @param timeout Wait timeout in milliseconds
1658 * @return Received file or null in case of failure
1659 */
1660 public File waitForFile(final long id, final int timeout)
1661 {
1662 int timeRemaining = timeout;
1663 File file = null;
1664
1665 while(timeRemaining > 0)
1666 {
1667 synchronized(receivedFiles)
1668 {
1669 NXCReceivedFile rf = receivedFiles.get(id);
1670 if (rf != null)
1671 {
1672 if (rf.getStatus() != NXCReceivedFile.OPEN)
1673 {
1674 if (rf.getStatus() == NXCReceivedFile.RECEIVED)
1675 file = rf.getFile();
1676 break;
1677 }
1678 }
1679
1680 long startTime = System.currentTimeMillis();
1681 try
1682 {
1683 receivedFiles.wait(timeRemaining);
1684 }
1685 catch(InterruptedException e)
1686 {
1687 }
1688 timeRemaining -= System.currentTimeMillis() - startTime;
1689 }
1690 }
1691 return file;
1692 }
1693
1694 /**
1695 * Wait for specific file tail to arrive
1696 *
1697 * @param fileName Waiting file name
1698 * @param timeout Wait timeout in milliseconds
1699 * @return Received tail string or null in case of failure
1700 */
1701 public String waitForFileTail(String fileName, final int timeout)
1702 {
1703 int timeRemaining = timeout;
1704 String tail = null;
1705
1706 while(timeRemaining > 0)
1707 {
1708 synchronized(recievedUpdates)
1709 {
1710 tail = recievedUpdates.get(fileName);
1711 if (tail != null)
1712 {
1713 recievedUpdates.remove(fileName);
1714 break;
1715 }
1716
1717 long startTime = System.currentTimeMillis();
1718 try
1719 {
1720 recievedUpdates.wait(timeRemaining);
1721 }
1722 catch(InterruptedException e)
1723 {
1724 }
1725 timeRemaining -= System.currentTimeMillis() - startTime;
1726 }
1727 }
1728 return tail;
1729 }
1730
1731 /**
1732 * Execute simple commands (without arguments and only returning RCC)
1733 *
1734 * @param command Command code
1735 * @throws IOException if socket I/O error occurs
1736 * @throws NXCException if NetXMS server returns an error or operation was timed out
1737 */
1738 protected void executeSimpleCommand(int command) throws IOException, NXCException
1739 {
1740 final NXCPMessage msg = newMessage(command);
1741 sendMessage(msg);
1742 waitForRCC(msg.getMessageId());
1743 }
1744
1745 /**
1746 * Receive table from server.
1747 *
1748 * @param requestId request ID
1749 * @param msgCode Message code
1750 * @return Received table
1751 * @throws NXCException if operation was timed out
1752 */
1753 public Table receiveTable(long requestId, int msgCode) throws NXCException
1754 {
1755 NXCPMessage msg = waitForMessage(msgCode, requestId);
1756 Table table = new Table(msg);
1757 while(!msg.isEndOfSequence())
1758 {
1759 msg = waitForMessage(msgCode, requestId);
1760 table.addDataFromMessage(msg);
1761 }
1762 return table;
1763 }
1764
1765 /**
1766 * Connect to NetMS server. Establish connection with the server and set up encryption if required.
1767 * Only base protocol version check will be performed. Login must be performed before using session
1768 * after successful connect.
1769 *
1770 * @throws IOException if socket I/O error occurs
1771 * @throws UnknownHostException if the host is unknown
1772 * @throws NXCException if NetXMS server returns an error or operation was timed out
1773 * @throws IllegalStateException if the state is illegal
1774 */
1775 public void connect() throws IOException, UnknownHostException, NXCException, IllegalStateException
1776 {
1777 connect(null);
1778 }
1779
1780 /**
1781 * Connect to NetMS server. Establish connection with the server and set up encryption if required.
1782 * Versions of protocol components given in *componentVersions* will be validated. Login must be
1783 * performed before using session after successful connect.
1784 *
1785 * @param componentVersions The versions of the components
1786 * @throws IOException if socket I/O error occurs
1787 * @throws UnknownHostException if the host is unknown
1788 * @throws NXCException if NetXMS server returns an error or operation was timed out
1789 * @throws IllegalStateException if the state is illegal
1790 */
1791 public void connect(int[] componentVersions) throws IOException, UnknownHostException, NXCException, IllegalStateException
1792 {
1793 if (connected)
1794 throw new IllegalStateException("Session already connected");
1795
1796 if (disconnected)
1797 throw new IllegalStateException("Session already disconnected and cannot be reused");
1798
1799 encryptionContext = null;
1800 Logger.info("NXCSession.connect", "Connecting to " + connAddress + ":" + connPort);
1801 try
1802 {
1803 socket = new Socket();
1804 socket.connect(new InetSocketAddress(connAddress, connPort), connectTimeout);
1805 msgWaitQueue = new NXCPMsgWaitQueue(commandTimeout);
1806 recvThread = new ReceiverThread();
1807 housekeeperThread = new HousekeeperThread();
1808 new NotificationProcessor();
1809
1810 // get server information
1811 Logger.debug("NXCSession.connect", "connection established, retrieving server info");
1812 NXCPMessage request = newMessage(NXCPCodes.CMD_GET_SERVER_INFO);
1813 sendMessage(request);
1814 NXCPMessage response = waitForMessage(NXCPCodes.CMD_REQUEST_COMPLETED, request.getMessageId());
1815
1816 protocolVersion = new ProtocolVersion(response);
1817 if (!ignoreProtocolVersion)
1818 {
1819 if (!protocolVersion.isCorrectVersion(ProtocolVersion.INDEX_BASE) ||
1820 ((componentVersions != null) && !validateProtocolVersions(componentVersions)))
1821 {
1822 Logger.warning("NXCSession.connect", "connection failed (" + protocolVersion.toString() + ")");
1823 throw new NXCException(RCC.BAD_PROTOCOL);
1824 }
1825 }
1826 else
1827 {
1828 Logger.debug("NXCSession.connect", "protocol version ignored");
1829 }
1830
1831 serverVersion = response.getFieldAsString(NXCPCodes.VID_SERVER_VERSION);
1832 serverId = response.getFieldAsInt64(NXCPCodes.VID_SERVER_ID);
1833 serverTimeZone = response.getFieldAsString(NXCPCodes.VID_TIMEZONE);
1834 serverTime = response.getFieldAsInt64(NXCPCodes.VID_TIMESTAMP) * 1000;
1835 serverTimeRecvTime = System.currentTimeMillis();
1836 serverChallenge = response.getFieldAsBinary(NXCPCodes.VID_CHALLENGE);
1837
1838 tileServerURL = response.getFieldAsString(NXCPCodes.VID_TILE_SERVER_URL);
1839 if (tileServerURL != null)
1840 {
1841 if (!tileServerURL.endsWith("/")) tileServerURL = tileServerURL.concat("/");
1842 }
1843 else
1844 {
1845 tileServerURL = "http://tile.openstreetmap.org/";
1846 }
1847
1848 dateFormat = response.getFieldAsString(NXCPCodes.VID_DATE_FORMAT);
1849 if ((dateFormat == null) || (dateFormat.length() == 0))
1850 dateFormat = "dd.MM.yyyy";
1851
1852 timeFormat = response.getFieldAsString(NXCPCodes.VID_TIME_FORMAT);
1853 if ((timeFormat == null) || (timeFormat.length() == 0))
1854 timeFormat = "HH:mm:ss";
1855
1856 shortTimeFormat = response.getFieldAsString(NXCPCodes.VID_SHORT_TIME_FORMAT);
1857 if ((shortTimeFormat == null) || (shortTimeFormat.length() == 0))
1858 shortTimeFormat = "HH:mm";
1859
1860 int count = response.getFieldAsInt32(NXCPCodes.VID_NUM_COMPONENTS);
1861 long fieldId = NXCPCodes.VID_COMPONENT_LIST_BASE;
1862 for(int i = 0; i < count; i++)
1863 serverComponents.add(response.getFieldAsString(fieldId++));
1864
1865 // Setup encryption if required
1866 if (connUseEncryption)
1867 {
1868 request = newMessage(NXCPCodes.CMD_REQUEST_ENCRYPTION);
1869 request.setFieldInt16(NXCPCodes.VID_USE_X509_KEY_FORMAT, 1);
1870 sendMessage(request);
1871 waitForRCC(request.getMessageId());
1872 }
1873
1874 Logger.debug("NXCSession.connect", "Connected to server version " + serverVersion);
1875 connected = true;
1876 }
1877 finally
1878 {
1879 if (!connected)
1880 disconnect(SessionNotification.USER_DISCONNECT);
1881 }
1882 }
1883
1884 /**
1885 * Login to server using login name and password.
1886 *
1887 * @param login login name
1888 * @param password password
1889 * @throws IOException if socket I/O error occurs
1890 * @throws NXCException if NetXMS server returns an error or operation was timed out
1891 * @throws IllegalStateException if the state is illegal
1892 */
1893 public void login(String login, String password) throws NXCException, IOException, IllegalStateException
1894 {
1895 login(AuthenticationType.PASSWORD, login, password, null, null);
1896 }
1897
1898 /**
1899 * Login to server using certificate.
1900 *
1901 * @param login login name
1902 * @param certificate user's certificate
1903 * @param signature user's digital signature
1904 * @throws IOException if socket I/O error occurs
1905 * @throws NXCException if NetXMS server returns an error or operation was timed out
1906 * @throws IllegalStateException if the state is illegal
1907 */
1908 public void login(String login, Certificate certificate, Signature signature) throws NXCException, IOException, IllegalStateException
1909 {
1910 login(AuthenticationType.CERTIFICATE, login, null, certificate, signature);
1911 }
1912
1913 /**
1914 * Login to server.
1915 *
1916 * @param authType authentication type
1917 * @param login login name
1918 * @param password password
1919 * @param certificate user's certificate
1920 * @param signature user's digital signature
1921 * @throws IOException if socket I/O error occurs
1922 * @throws NXCException if NetXMS server returns an error or operation was timed out
1923 * @throws IllegalStateException if the state is illegal
1924 */
1925 public void login(AuthenticationType authType, String login, String password, Certificate certificate, Signature signature) throws NXCException, IOException, IllegalStateException
1926 {
1927 if (!connected)
1928 throw new IllegalStateException("Session not connected");
1929
1930 if (disconnected)
1931 throw new IllegalStateException("Session already disconnected and cannot be reused");
1932
1933 authenticationMethod = authType;
1934 userName = login;
1935
1936 final NXCPMessage request = newMessage(NXCPCodes.CMD_LOGIN);
1937 request.setFieldInt16(NXCPCodes.VID_AUTH_TYPE, authType.getValue());
1938 request.setField(NXCPCodes.VID_LOGIN_NAME, login);
1939
1940 if ((authType == AuthenticationType.PASSWORD) || (authType == AuthenticationType.SSO_TICKET))
1941 {
1942 request.setField(NXCPCodes.VID_PASSWORD, password);
1943 }
1944 else if (authType == AuthenticationType.CERTIFICATE)
1945 {
1946 if ((serverChallenge == null) || (signature == null) || (certificate == null))
1947 {
1948 throw new NXCException(RCC.ENCRYPTION_ERROR);
1949 }
1950 byte[] signedChallenge = signChallenge(signature, serverChallenge);
1951 request.setField(NXCPCodes.VID_SIGNATURE, signedChallenge);
1952 try
1953 {
1954 request.setField(NXCPCodes.VID_CERTIFICATE, certificate.getEncoded());
1955 }
1956 catch(CertificateEncodingException e)
1957 {
1958 throw new NXCException(RCC.ENCRYPTION_ERROR);
1959 }
1960 }
1961
1962 request.setField(NXCPCodes.VID_LIBNXCL_VERSION, NXCommon.VERSION);
1963 request.setField(NXCPCodes.VID_CLIENT_INFO, connClientInfo);
1964 request.setField(NXCPCodes.VID_OS_INFO, System.getProperty("os.name") + " " + System.getProperty("os.version"));
1965 request.setFieldInt16(NXCPCodes.VID_CLIENT_TYPE, clientType);
1966 if (clientAddress != null)
1967 request.setField(NXCPCodes.VID_CLIENT_ADDRESS, clientAddress);
1968 if (clientLanguage != null)
1969 request.setField(NXCPCodes.VID_LANGUAGE, clientLanguage);
1970 request.setFieldInt16(NXCPCodes.VID_ENABLE_COMPRESSION, 1);
1971 sendMessage(request);
1972
1973 final NXCPMessage response = waitForMessage(NXCPCodes.CMD_LOGIN_RESP, request.getMessageId());
1974 int rcc = response.getFieldAsInt32(NXCPCodes.VID_RCC);
1975 Logger.debug("NXCSession.login", "CMD_LOGIN_RESP received, RCC=" + rcc);
1976 if (rcc != RCC.SUCCESS)
1977 {
1978 Logger.warning("NXCSession.login", "Login failed, RCC=" + rcc);
1979 throw new NXCException(rcc);
1980 }
1981 userId = response.getFieldAsInt32(NXCPCodes.VID_USER_ID);
1982 sessionId = response.getFieldAsInt32(NXCPCodes.VID_SESSION_ID);
1983 userSystemRights = response.getFieldAsInt64(NXCPCodes.VID_USER_SYS_RIGHTS);
1984 passwordExpired = response.getFieldAsBoolean(NXCPCodes.VID_CHANGE_PASSWD_FLAG);
1985 graceLogins = response.getFieldAsInt32(NXCPCodes.VID_GRACE_LOGINS);
1986 zoningEnabled = response.getFieldAsBoolean(NXCPCodes.VID_ZONING_ENABLED);
1987 helpdeskLinkActive = response.getFieldAsBoolean(NXCPCodes.VID_HELPDESK_LINK_ACTIVE);
1988
1989 defaultDciPollingInterval = response.getFieldAsInt32(NXCPCodes.VID_POLLING_INTERVAL);
1990 if (defaultDciPollingInterval == 0)
1991 defaultDciPollingInterval = 60;
1992
1993 defaultDciRetentionTime = response.getFieldAsInt32(NXCPCodes.VID_RETENTION_TIME);
1994 if (defaultDciRetentionTime == 0)
1995 defaultDciRetentionTime = 30;
1996
1997 minViewRefreshInterval = response.getFieldAsInt32(NXCPCodes.VID_VIEW_REFRESH_INTERVAL);
1998 if (minViewRefreshInterval <= 0)
1999 minViewRefreshInterval = 200;
2000
2001 strictAlarmStatusFlow = response.getFieldAsBoolean(NXCPCodes.VID_ALARM_STATUS_FLOW_STATE);
2002 timedAlarmAckEnabled = response.getFieldAsBoolean(NXCPCodes.VID_TIMED_ALARM_ACK_ENABLED);
2003
2004 serverCommandOutputTimeout = response.getFieldAsInt32(NXCPCodes.VID_SERVER_COMMAND_TIMEOUT) * 1000;
2005 serverColor = response.getFieldAsString(NXCPCodes.VID_SERVER_COLOR);
2006 serverName = response.getFieldAsString(NXCPCodes.VID_SERVER_NAME);
2007 if ((serverName == null) || serverName.isEmpty())
2008 serverName = connAddress;
2009
2010 alarmListDisplayLimit = response.getFieldAsInt32(NXCPCodes.VID_ALARM_LIST_DISP_LIMIT);
2011 Logger.info("NXCSession.connect", "alarmListDisplayLimit = " + alarmListDisplayLimit);
2012
2013 messageOfTheDay = response.getFieldAsString(NXCPCodes.VID_MESSAGE_OF_THE_DAY);
2014
2015 allowCompression = response.getFieldAsBoolean(NXCPCodes.VID_ENABLE_COMPRESSION);
2016
2017 Logger.info("NXCSession.connect", "succesfully logged in, userId=" + userId);
2018 }
2019
2020 /**
2021 * Disconnect session in background
2022 *
2023 * @param reason disconnect reason (appropriate session notification code)
2024 */
2025 private void backgroundDisconnect(final int reason)
2026 {
2027 Thread t = new Thread(new Runnable() {
2028 @Override
2029 public void run()
2030 {
2031 disconnect(reason);
2032 }
2033 }, "NXCSession disconnect");
2034 t.setDaemon(true);
2035 t.start();
2036 }
2037
2038 /**
2039 * Disconnect from server.
2040 *
2041 * @param reason disconnect reason (appropriate session notification code)
2042 */
2043 synchronized private void disconnect(int reason)
2044 {
2045 if (disconnected)
2046 return;
2047
2048 if (socket != null)
2049 {
2050 try
2051 {
2052 socket.shutdownInput();
2053 socket.shutdownOutput();
2054 }
2055 catch(IOException e)
2056 {
2057 }
2058
2059 try
2060 {
2061 socket.close();
2062 }
2063 catch(IOException e)
2064 {
2065 }
2066 }
2067
2068 // cause notification processing thread to stop
2069 notificationQueue.clear();
2070 if (reason != SessionNotification.USER_DISCONNECT)
2071 notificationQueue.offer(new SessionNotification(reason));
2072 notificationQueue.offer(new SessionNotification(SessionNotification.STOP_PROCESSING_THREAD));
2073
2074 if (recvThread != null)
2075 {
2076 while(recvThread.isAlive())
2077 {
2078 try
2079 {
2080 recvThread.join();
2081 }
2082 catch(InterruptedException e)
2083 {
2084 }
2085 }
2086 recvThread = null;
2087 }
2088
2089 if (housekeeperThread != null)
2090 {
2091 housekeeperThread.setStopFlag(true);
2092 while(housekeeperThread.isAlive())
2093 {
2094 try
2095 {
2096 housekeeperThread.join();
2097 }
2098 catch(InterruptedException e)
2099 {
2100 }
2101 }
2102 housekeeperThread = null;
2103 }
2104
2105 if (msgWaitQueue != null)
2106 {
2107 msgWaitQueue.shutdown();
2108 msgWaitQueue = null;
2109 }
2110
2111 connected = false;
2112 disconnected = true;
2113 socket = null;
2114
2115 listeners.clear();
2116 consoleListeners.clear();
2117 messageSubscriptions.clear();
2118 receivedFiles.clear();
2119 recievedUpdates.clear();
2120 objectList.clear();
2121 objectListGUID.clear();
2122 zoneList.clear();
2123 eventObjects.clear();
2124 userDB.clear();
2125 alarmCategories.clear();
2126 }
2127
2128 /**
2129 * Disconnect from server.
2130 */
2131 public void disconnect()
2132 {
2133 disconnect(SessionNotification.USER_DISCONNECT);
2134 }
2135
2136 /**
2137 * Get connection state
2138 * @return connection state
2139 */
2140 public boolean isConnected()
2141 {
2142 return connected;
2143 }
2144
2145 /**
2146 * Get encryption state for current session.
2147 *
2148 * @return true if session is encrypted
2149 */
2150 public boolean isEncrypted()
2151 {
2152 return connUseEncryption;
2153 }
2154
2155 /**
2156 * Get the state of protocol version check
2157 *
2158 * @return true if protocol version should not be checked
2159 */
2160 public boolean isIgnoreProtocolVersion()
2161 {
2162 return ignoreProtocolVersion;
2163 }
2164
2165 /**
2166 * If set to true, protocol version is not checked at connect.
2167 *
2168 * @param ignoreProtocolVersion true if protocol version should not be checked
2169 */
2170 public void setIgnoreProtocolVersion(boolean ignoreProtocolVersion)
2171 {
2172 this.ignoreProtocolVersion = ignoreProtocolVersion;
2173 }
2174
2175 /**
2176 * Validate protocol versions
2177 *
2178 * @param versions the protocol versions
2179 * @return true if protocol versions are valid
2180 */
2181 public boolean validateProtocolVersions(int[] versions)
2182 {
2183 if (protocolVersion == null)
2184 return false;
2185 for(int index : versions)
2186 if (!protocolVersion.isCorrectVersion(index))
2187 return false;
2188 return true;
2189 }
2190
2191 /**
2192 * Get default receiver buffer size.
2193 *
2194 * @return Default receiver buffer size in bytes.
2195 */
2196 public int getDefaultRecvBufferSize()
2197 {
2198 return defaultRecvBufferSize;
2199 }
2200
2201 /**
2202 * Get max receiver buffer size.
2203 *
2204 * @return Max receiver buffer size in bytes.
2205 */
2206 public int getMaxRecvBufferSize()
2207 {
2208 return maxRecvBufferSize;
2209 }
2210
2211 /**
2212 * Set receiver buffer size. This method should be called before connect(). It will not have any effect after
2213 * connect().
2214 *
2215 * @param defaultBufferSize default size of receiver buffer in bytes.
2216 * @param maxBufferSize max size of receiver buffer in bytes.
2217 */
2218 public void setRecvBufferSize(int defaultBufferSize, int maxBufferSize)
2219 {
2220 this.defaultRecvBufferSize = defaultBufferSize;
2221 this.maxRecvBufferSize = maxBufferSize;
2222 }
2223
2224 /**
2225 * Get server address
2226 *
2227 * @return Server address
2228 */
2229 public String getServerAddress()
2230 {
2231 return connAddress;
2232 }
2233
2234 /**
2235 * Get NetXMS server version.
2236 *
2237 * @return Server version
2238 */
2239 public String getServerVersion()
2240 {
2241 return serverVersion;
2242 }
2243
2244 /**
2245 * Get NetXMS server UID.
2246 *
2247 * @return Server UID
2248 */
2249 public long getServerId()
2250 {
2251 return serverId;
2252 }
2253
2254 /**
2255 * Get server time zone.
2256 *
2257 * @return server's time zone string
2258 */
2259 public String getServerTimeZone()
2260 {
2261 return serverTimeZone;
2262 }
2263
2264 /**
2265 * Get server name
2266 *
2267 * @return the serverName
2268 */
2269 public String getServerName()
2270 {
2271 return serverName;
2272 }
2273
2274 /**
2275 * Get server identification colour
2276 *
2277 * @return the serverColor
2278 */
2279 public String getServerColor()
2280 {
2281 return serverColor;
2282 }
2283
2284 /**
2285 * Get server time
2286 *
2287 * @return the serverTime
2288 */
2289 public long getServerTime()
2290 {
2291 long offset = System.currentTimeMillis() - serverTimeRecvTime;
2292 return serverTime + offset;
2293 }
2294
2295 /**
2296 * @return the serverChallenge
2297 */
2298 public byte[] getServerChallenge()
2299 {
2300 return serverChallenge;
2301 }
2302
2303 /**
2304 * Check if server component with given id is registered
2305 *
2306 * @param componentId The component ID
2307 * @return true if server component is registered
2308 */
2309 public boolean isServerComponentRegistered(String componentId)
2310 {
2311 return serverComponents.contains(componentId);
2312 }
2313
2314 /**
2315 * Get list of registered server components
2316 *
2317 * @return Array of registered server components
2318 */
2319 public String[] getRegisteredServerComponents()
2320 {
2321 return serverComponents.toArray(new String[serverComponents.size()]);
2322 }
2323
2324 /**
2325 * Get server URL
2326 *
2327 * @return the tileServerURL
2328 */
2329 public String getTileServerURL()
2330 {
2331 return tileServerURL;
2332 }
2333
2334 /**
2335 * Get the state of zoning
2336 *
2337 * @return true if zoning is enabled
2338 */
2339 public boolean isZoningEnabled()
2340 {
2341 return zoningEnabled;
2342 }
2343
2344 /**
2345 * Get status of helpdesk integration module on server.
2346 *
2347 * @return true if helpdesk integration module loaded on server
2348 */
2349 public boolean isHelpdeskLinkActive()
2350 {
2351 return helpdeskLinkActive;
2352 }
2353
2354 /**
2355 * Get client information string
2356 *
2357 * @return The client information
2358 */
2359 public String getClientInfo()
2360 {
2361 return connClientInfo;
2362 }
2363
2364 /**
2365 * Set client information string
2366 *
2367 * @param connClientInfo The client info to set
2368 */
2369 public void setClientInfo(final String connClientInfo)
2370 {
2371 this.connClientInfo = connClientInfo;
2372 }
2373
2374 /**
2375 * Set command execution timeout.
2376 *
2377 * @param commandTimeout
2378 * New command timeout
2379 */
2380 public void setCommandTimeout(final int commandTimeout)
2381 {
2382 this.commandTimeout = commandTimeout;
2383 }
2384
2385 /**
2386 * Set connect call timeout (must be set before connect call)
2387 *
2388 * @param connectTimeout connect timeout in milliseconds
2389 */
2390 public void setConnectTimeout(int connectTimeout)
2391 {
2392 this.connectTimeout = connectTimeout;
2393 }
2394
2395 /**
2396 * Get identifier of logged in user.
2397 *
2398 * @return Identifier of logged in user
2399 */
2400 public int getUserId()
2401 {
2402 return userId;
2403 }
2404
2405 /**
2406 * Get the current user name
2407 *
2408 * @return the userName
2409 */
2410 public String getUserName()
2411 {
2412 return userName;
2413 }
2414
2415 /**
2416 * Get the current authentication method
2417 *
2418 * @return the authenticationMethod
2419 */
2420 public AuthenticationType getAuthenticationMethod()
2421 {
2422 return authenticationMethod;
2423 }
2424
2425 /**
2426 * Get system-wide rights of currently logged in user.
2427 *
2428 * @return System-wide rights of currently logged in user
2429 */
2430 public long getUserSystemRights()
2431 {
2432 return userSystemRights;
2433 }
2434
2435 /** Get message of the day if server config is set
2436 * @return Message of the day
2437 */
2438 public String getMessageOfTheDay()
2439 {
2440 return messageOfTheDay;
2441 }
2442
2443 /**
2444 * Check if password is expired for currently logged in user.
2445 *
2446 * @return true if password is expired
2447 */
2448 public boolean isPasswordExpired()
2449 {
2450 return passwordExpired;
2451 }
2452
2453 /**
2454 * Get number of remaining grace logins
2455 *
2456 * @return number of remaining grace logins
2457 */
2458 public int getGraceLogins()
2459 {
2460 return graceLogins;
2461 }
2462
2463 /**
2464 * Get maximum number of records allowed to be displayed in alarm list
2465 *
2466 * @return The limit of alarms displayed
2467 */
2468 public int getAlarmListDisplayLimit()
2469 {
2470 return alarmListDisplayLimit;
2471 }
2472
2473 /**
2474 * Synchronizes NetXMS objects between server and client. After successful
2475 * sync, subscribe client to object change notifications.
2476 *
2477 * @throws IOException if socket I/O error occurs
2478 * @throws NXCException if NetXMS server returns an error or operation was timed out
2479 */
2480 public synchronized void syncObjects() throws IOException, NXCException
2481 {
2482 syncObjects.acquireUninterruptibly();
2483 NXCPMessage msg = newMessage(NXCPCodes.CMD_GET_OBJECTS);
2484 msg.setFieldInt16(NXCPCodes.VID_SYNC_COMMENTS, 1);
2485 sendMessage(msg);
2486 waitForRCC(msg.getMessageId());
2487 waitForSync(syncObjects, commandTimeout * 10);
2488 objectsSynchronized = true;
2489 sendNotification(new SessionNotification(SessionNotification.OBJECT_SYNC_COMPLETED));
2490 subscribe(CHANNEL_OBJECTS);
2491 }
2492
2493 /**
2494 * Synchronizes selected object set with the server.
2495 *
2496 * @param objects identifiers of objects need to be synchronized
2497 * @param syncComments if true, comments for objects will be synchronized as well
2498 * @throws IOException if socket I/O error occurs
2499 * @throws NXCException if NetXMS server returns an error or operation was timed out
2500 */
2501 public void syncObjectSet(long[] objects, boolean syncComments) throws IOException, NXCException
2502 {
2503 syncObjectSet(objects, syncComments, 0);
2504 }
2505
2506 /**
2507 * Synchronizes selected object set with the server. The following options are accepted:
2508 * OBJECT_SYNC_NOTIFY - send object update notification for each received object
2509 * OBJECT_SYNC_WAIT - wait until all requested objects received
2510 *
2511 * @param objects identifiers of objects need to be synchronized
2512 * @param syncComments if true, comments for objects will be synchronized as well
2513 * @param options sync options (see above)
2514 * @throws IOException if socket I/O error occurs
2515 * @throws NXCException if NetXMS server returns an error or operation was timed out
2516 */
2517 public void syncObjectSet(long[] objects, boolean syncComments, int options) throws IOException, NXCException
2518 {
2519 NXCPMessage msg = newMessage(NXCPCodes.CMD_GET_SELECTED_OBJECTS);
2520 msg.setFieldInt16(NXCPCodes.VID_SYNC_COMMENTS, syncComments ? 1 : 0);
2521 msg.setFieldInt16(NXCPCodes.VID_FLAGS, options);
2522 msg.setFieldInt32(NXCPCodes.VID_NUM_OBJECTS, objects.length);
2523 msg.setField(NXCPCodes.VID_OBJECT_LIST, objects);
2524 sendMessage(msg);
2525 waitForRCC(msg.getMessageId());
2526
2527 if ((options & OBJECT_SYNC_WAIT) != 0) waitForRCC(msg.getMessageId());
2528 }
2529
2530 /**
2531 * Synchronize only those objects from given set which are not synchronized yet.
2532 *
2533 * @param objects identifiers of objects need to be synchronized
2534 * @param syncComments if true, comments for objects will be synchronized as well
2535 * @throws IOException if socket I/O error occurs
2536 * @throws NXCException if NetXMS server returns an error or operation was timed out
2537 */
2538 public void syncMissingObjects(long[] objects, boolean syncComments) throws IOException, NXCException
2539 {
2540 syncMissingObjects(objects, syncComments, 0);
2541 }
2542
2543 /**
2544 * Synchronize only those objects from given set which are not synchronized yet.
2545 * Accepts all options which are valid for syncObjectSet.
2546 *
2547 * @param objects identifiers of objects need to be synchronized
2548 * @param syncComments if true, comments for objects will be synchronized as well
2549 * @param options sync options (see comments for syncObjectSet)
2550 * @throws IOException if socket I/O error occurs
2551 * @throws NXCException if NetXMS server returns an error or operation was timed out
2552 */
2553 public void syncMissingObjects(long[] objects, boolean syncComments, int options) throws IOException, NXCException
2554 {
2555 final long[] syncList = Arrays.copyOf(objects, objects.length);
2556 int count = syncList.length;
2557 synchronized(objectList)
2558 {
2559 for(int i = 0; i < syncList.length; i++)
2560 {
2561 if (objectList.containsKey(syncList[i]))
2562 {
2563 syncList[i] = 0;
2564 count--;
2565 }
2566 }
2567 }
2568
2569 if (count > 0)
2570 {
2571 syncObjectSet(syncList, syncComments, options);
2572 }
2573 }
2574
2575 /**
2576 * Find NetXMS object by it's identifier.
2577 *
2578 * @param id Object identifier
2579 * @return Object with given ID or null if object cannot be found
2580 */
2581 public AbstractObject findObjectById(final long id)
2582 {
2583 synchronized(objectList)
2584 {
2585 return objectList.get(id);
2586 }
2587 }
2588
2589 /**
2590 * Find NetXMS object by it's identifier with additional class checking.
2591 *
2592 * @param id object identifier
2593 * @param requiredClass required object class
2594 * @param <T> Object
2595 * @return Object with given ID or null if object cannot be found or is not an instance of required class
2596 */
2597 @SuppressWarnings("unchecked")
2598 public <T> T findObjectById(final long id, final Class<T> requiredClass)
2599 {
2600 AbstractObject object = findObjectById(id);
2601 return requiredClass.isInstance(object) ? (T)object : null;
2602 }
2603
2604 /**
2605 * Find multiple NetXMS objects by identifiers
2606 *
2607 * @param idList array of object identifiers
2608 * @param returnUnknown if true, this method will return UnknownObject placeholders for unknown object identifiers
2609 * @return list of found objects
2610 */
2611 public List<AbstractObject> findMultipleObjects(final long[] idList, boolean returnUnknown)
2612 {
2613 return findMultipleObjects(idList, null, returnUnknown);
2614 }
2615
2616 /**
2617 * Find multiple NetXMS objects by identifiers
2618 *
2619 * @param idList array of object identifiers
2620 * @param classFilter class filter for objects, or null to disable filtering
2621 * @param returnUnknown if true, this method will return UnknownObject placeholders for unknown object identifiers
2622 * @return list of found objects
2623 */
2624 public List<AbstractObject> findMultipleObjects(final long[] idList, Class<? extends AbstractObject> classFilter, boolean returnUnknown)
2625 {
2626 List<AbstractObject> result = new ArrayList<AbstractObject>(idList.length);
2627
2628 synchronized(objectList)
2629 {
2630 for(int i = 0; i < idList.length; i++)
2631 {
2632 final AbstractObject object = objectList.get(idList[i]);
2633 if ((object != null) && ((classFilter == null) || classFilter.isInstance(object)))
2634 {
2635 result.add(object);
2636 }
2637 else if (returnUnknown)
2638 {
2639 result.add(new UnknownObject(idList[i], this));
2640 }
2641 }
2642 }
2643
2644 return result;
2645 }
2646
2647 /**
2648 * Find multiple NetXMS objects by identifiers
2649 *
2650 * @param idList array of object identifiers
2651 * @param returnUnknown if true, this method will return UnknownObject placeholders for unknown object identifiers
2652 * @return array of found objects
2653 */
2654 public List<AbstractObject> findMultipleObjects(final Long[] idList, boolean returnUnknown)
2655 {
2656 return findMultipleObjects(idList, null, returnUnknown);
2657 }
2658
2659 /**
2660 * Find multiple NetXMS objects by identifiers
2661 *
2662 * @param idList array of object identifiers
2663 * @param classFilter class filter for objects, or null to disable filtering
2664 * @param returnUnknown if true, this method will return UnknownObject placeholders for unknown object identifiers
2665 * @return array of found objects
2666 */
2667 public List<AbstractObject> findMultipleObjects(final Long[] idList, Class<? extends AbstractObject> classFilter, boolean returnUnknown)
2668 {
2669 List<AbstractObject> result = new ArrayList<AbstractObject>(idList.length);
2670
2671 synchronized(objectList)
2672 {
2673 for(int i = 0; i < idList.length; i++)
2674 {
2675 final AbstractObject object = objectList.get(idList[i]);
2676 if ((object != null) && ((classFilter == null) || classFilter.isInstance(object)))
2677 {
2678 result.add(object);
2679 }
2680 else if (returnUnknown)
2681 {
2682 result.add(new UnknownObject(idList[i], this));
2683 }
2684 }
2685 }
2686
2687 return result;
2688 }
2689
2690 /**
2691 * Find NetXMS object by it's GUID.
2692 *
2693 * @param guid Object GUID
2694 * @return Object with given ID or null if object cannot be found
2695 */
2696 public AbstractObject findObjectByGUID(final UUID guid)
2697 {
2698 synchronized(objectList)
2699 {
2700 return objectListGUID.get(guid);
2701 }
2702 }
2703
2704 /**
2705 * Find NetXMS object by it's GUID with additional class checking.
2706 *
2707 * @param guid object GUID
2708 * @param requiredClass required object class
2709 * @param <T> Object
2710 * @return Object with given ID or null if object cannot be found or is not an instance of required class
2711 */
2712 @SuppressWarnings("unchecked")
2713 public <T extends AbstractObject> T findObjectByGUID(final UUID guid, final Class<T> requiredClass)
2714 {
2715 AbstractObject object = findObjectByGUID(guid);
2716 return requiredClass.isInstance(object) ? (T)object : null;
2717 }
2718
2719 /**
2720 * Find zone object by zone UIN (unique identification number).
2721 *
2722 * @param zoneUIN zone UIN to find
2723 * @return zone object or null
2724 */
2725 public Zone findZone(long zoneUIN)
2726 {
2727 synchronized(objectList)
2728 {
2729 return zoneList.get(zoneUIN);
2730 }
2731 }
2732
2733 /**
2734 * Get all accessible zone objects
2735 *
2736 * @return list of all accessible zone objects
2737 */
2738 public List<Zone> getAllZones()
2739 {
2740 synchronized(objectList)
2741 {
2742 return new ArrayList<Zone>(zoneList.values());
2743 }
2744 }
2745
2746 /**
2747 * Find object by name. If multiple objects with same name exist,
2748 * it is not determined what object will be returned. Name comparison
2749 * is case-insensitive.
2750 *
2751 * @param name object name to find
2752 * @return object with matching name or null
2753 */
2754 public AbstractObject findObjectByName(final String name)
2755 {
2756 AbstractObject result = null;
2757 synchronized(objectList)
2758 {
2759 for(AbstractObject object : objectList.values())
2760 {
2761 if (object.getObjectName().equalsIgnoreCase(name))
2762 {
2763 result = object;
2764 break;
2765 }
2766 }
2767 }
2768 return result;
2769 }
2770
2771 /**
2772 * Find object by name using regular expression. If multiple objects with same name exist,
2773 * it is not determined what object will be returned. Name comparison is case-insensitive.
2774 *
2775 * @param pattern regular expression for matching object name
2776 * @return object with matching name or null
2777 */
2778 public AbstractObject findObjectByNamePattern(final String pattern)
2779 {
2780 AbstractObject result = null;
2781 Matcher matcher = Pattern.compile(pattern).matcher("");
2782 synchronized(objectList)
2783 {
2784 for(AbstractObject object : objectList.values())
2785 {
2786 matcher.reset(object.getObjectName());
2787 if (matcher.matches())
2788 {
2789 result = object;
2790 break;
2791 }
2792 }
2793 }
2794 return result;
2795 }
2796
2797 /**
2798 * Generic object find using filter. WIll return first object matching given filter.
2799 *
2800 * @param filter ObjectFilter to filter the result
2801 * @return first matching object or null
2802 */
2803 public AbstractObject findObject(ObjectFilter filter)
2804 {
2805 AbstractObject result = null;
2806 synchronized(objectList)
2807 {
2808 for(AbstractObject object : objectList.values())
2809 {
2810 if (filter.filter(object))
2811 {
2812 result = object;
2813 break;
2814 }
2815 }
2816 }
2817 return result;
2818 }
2819
2820 /**
2821 * Find all objects matching given filter.
2822 *
2823 * @param filter ObjectFilter to filter the result
2824 * @return list of matching objects (empty list if nothing found)
2825 */
2826 public List<AbstractObject> filterObjects(ObjectFilter filter)
2827 {
2828 List<AbstractObject> result = new ArrayList<AbstractObject>();
2829 synchronized(objectList)
2830 {
2831 for(AbstractObject object : objectList.values())
2832 {
2833 if (filter.filter(object))
2834 {
2835 result.add(object);
2836 }
2837 }
2838 }
2839 return result;
2840 }
2841
2842 /**
2843 * Get list of top-level objects matching given class filter. Class filter
2844 * may be null to ignore object class.
2845 *
2846 * @param classFilter To filter the classes
2847 * @return List of all top matching level objects (either without parents or with
2848 * inaccessible parents)
2849 */
2850 public AbstractObject[] getTopLevelObjects(Set<Integer> classFilter)
2851 {
2852 HashSet<AbstractObject> list = new HashSet<AbstractObject>();
2853 synchronized(objectList)
2854 {
2855 for(AbstractObject object : objectList.values())
2856 {
2857 if ((classFilter != null) && !classFilter.contains(object.getObjectClass())) continue;
2858
2859 if (!object.hasParents())
2860 {
2861 list.add(object);
2862 }
2863 else
2864 {
2865 boolean hasParents = false;
2866 Iterator<Long> it = object.getParents();
2867 while(it.hasNext())
2868 {
2869 Long parent = it.next();
2870 if (classFilter != null)
2871 {
2872 AbstractObject p = objectList.get(parent);
2873 if ((p != null) && classFilter.contains(p.getObjectClass()))
2874 {
2875 hasParents = true;
2876 break;
2877 }
2878 }
2879 else
2880 {
2881 if (objectList.containsKey(parent))
2882 {
2883 hasParents = true;
2884 break;
2885 }
2886 }
2887 }
2888 if (!hasParents) list.add(object);
2889 }
2890 }
2891 }
2892 return list.toArray(new AbstractObject[list.size()]);
2893 }
2894
2895 /**
2896 * Get list of top-level objects.
2897 *
2898 * @return List of all top level objects (either without parents or with
2899 * inaccessible parents)
2900 */
2901 public AbstractObject[] getTopLevelObjects()
2902 {
2903 return getTopLevelObjects(null);
2904 }
2905
2906 /**
2907 * Get list of all objects
2908 *
2909 * @return List of all objects
2910 */
2911 public List<AbstractObject> getAllObjects()
2912 {
2913 synchronized(objectList)
2914 {
2915 return new ArrayList<AbstractObject>(objectList.values());
2916 }
2917 }
2918
2919 /**
2920 * Get object name by ID.
2921 *
2922 * @param objectId object ID
2923 * @return object name if object is known, or string in form [&lt;object_id&gt;] for unknown objects
2924 */
2925 public String getObjectName(long objectId)
2926 {
2927 AbstractObject object = findObjectById(objectId);
2928 return (object != null) ? object.getObjectName() : ("[" + Long.toString(objectId) + "]");
2929 }
2930
2931 /**
2932 * Get list of active alarms. For accessing terminated alarms log view API should be used.
2933 *
2934 * @return Hash map containing alarms
2935 * @throws IOException if socket I/O error occurs
2936 * @throws NXCException if NetXMS server returns an error or operation was timed out
2937 */
2938 public HashMap<Long, Alarm> getAlarms() throws IOException, NXCException
2939 {
2940 NXCPMessage msg = newMessage(NXCPCodes.CMD_GET_ALL_ALARMS);
2941 final long rqId = msg.getMessageId();
2942 sendMessage(msg);
2943
2944 final HashMap<Long, Alarm> alarmList = new HashMap<Long, Alarm>(0);
2945 while(true)
2946 {
2947 msg = waitForMessage(NXCPCodes.CMD_ALARM_DATA, rqId);
2948 long alarmId = msg.getFieldAsInt32(NXCPCodes.VID_ALARM_ID);
2949 if (alarmId == 0)
2950 break; // ALARM_ID == 0 indicates end of list
2951 alarmList.put(alarmId, new Alarm(msg));
2952 }
2953
2954 return alarmList;
2955 }
2956
2957 /**
2958 * Get information about single active alarm. Terminated alarms cannot be accessed with this call.
2959 *
2960 * @param alarmId alarm ID
2961 * @return alarm object
2962 * @throws IOException if socket I/O error occurs
2963 * @throws NXCException if NetXMS server returns an error or operation was timed out
2964 */
2965 public Alarm getAlarm(long alarmId) throws IOException, NXCException
2966 {
2967 NXCPMessage msg = newMessage(NXCPCodes.CMD_GET_ALARM);
2968 msg.setFieldInt32(NXCPCodes.VID_ALARM_ID, (int) alarmId);
2969 sendMessage(msg);
2970 final NXCPMessage response = waitForRCC(msg.getMessageId());
2971 return new Alarm(response);
2972 }
2973
2974 /**
2975 * Get information about events related to single active alarm. Information for terminated alarms cannot be accessed with this call.
2976 * User must have "view alarms" permission on alarm's source node and "view event log" system-wide access.
2977 *
2978 * @param alarmId alarm ID
2979 * @return list of related events
2980 * @throws IOException if socket I/O error occurs
2981 * @throws NXCException if NetXMS server returns an error or operation was timed out
2982 */
2983 public List<EventInfo> getAlarmEvents(long alarmId) throws IOException, NXCException
2984 {
2985 NXCPMessage msg = newMessage(NXCPCodes.CMD_GET_ALARM_EVENTS);
2986 msg.setFieldInt32(NXCPCodes.VID_ALARM_ID, (int) alarmId);
2987 sendMessage(msg);
2988 final NXCPMessage response = waitForRCC(msg.getMessageId());
2989
2990 int count = response.getFieldAsInt32(NXCPCodes.VID_NUM_ELEMENTS);
2991 List<EventInfo> list = new ArrayList<EventInfo>(count);
2992 long varId = NXCPCodes.VID_ELEMENT_LIST_BASE;
2993 for(int i = 0; i < count; i++)
2994 {
2995 EventInfo parent = null;
2996 long rootId = response.getFieldAsInt64(varId + 1);
2997 if (rootId != 0)
2998 {
2999 for(EventInfo e : list)
3000 {
3001 if (e.getId() == rootId)
3002 {
3003 parent = e;
3004 break;
3005 }
3006 }
3007 }
3008 list.add(new EventInfo(response, varId, parent));
3009 varId += 10;
3010 }
3011 return list;
3012 }
3013
3014 /**
3015 * Acknowledge alarm.
3016 *
3017 * @param alarmId Identifier of alarm to be acknowledged.
3018 * @param sticky if set to true, acknowledged state will be made "sticky" (duplicate alarms with same key will not revert it back to outstanding)
3019 * @param time timeout for sticky acknowledge in seconds (0 for infinite)
3020 * @throws IOException if socket I/O error occurs
3021 * @throws NXCException if NetXMS server returns an error or operation was timed out
3022 */
3023 public void acknowledgeAlarm(final long alarmId, boolean sticky, int time) throws IOException, NXCException
3024 {
3025 NXCPMessage msg = newMessage(NXCPCodes.CMD_ACK_ALARM);
3026 msg.setFieldInt32(NXCPCodes.VID_ALARM_ID, (int) alarmId);
3027 msg.setFieldInt16(NXCPCodes.VID_STICKY_FLAG, sticky ? 1 : 0);
3028 msg.setFieldInt32(NXCPCodes.VID_TIMESTAMP, time);
3029 sendMessage(msg);
3030 waitForRCC(msg.getMessageId());
3031 }
3032
3033 /**
3034 * Acknowledge alarm.
3035 *
3036 * @param alarmId Identifier of alarm to be acknowledged.
3037 * @throws IOException if socket I/O error occurs
3038 * @throws NXCException if NetXMS server returns an error or operation was timed out
3039 */
3040 public void acknowledgeAlarm(final long alarmId) throws IOException, NXCException
3041 {
3042 acknowledgeAlarm(alarmId, false, 0);
3043 }
3044
3045 /**
3046 * Acknowledge alarm by helpdesk reference.
3047 *
3048 * @param helpdeskReference Helpdesk issue reference (e.g. JIRA issue key)
3049 * @throws IOException if socket I/O error occurs
3050 * @throws NXCException if NetXMS server returns an error or operation was timed out
3051 */
3052 public void acknowledgeAlarm(String helpdeskReference) throws IOException, NXCException
3053 {
3054 NXCPMessage msg = newMessage(NXCPCodes.CMD_ACK_ALARM);
3055 msg.setField(NXCPCodes.VID_HELPDESK_REF, helpdeskReference);
3056 sendMessage(msg);
3057 waitForRCC(msg.getMessageId());
3058 }
3059
3060 /**
3061 * Resolve alarm.
3062 *
3063 * @param alarmId Identifier of alarm to be resolved.
3064 * @throws IOException if socket I/O error occurs
3065 * @throws NXCException if NetXMS server returns an error or operation was timed out
3066 */
3067 public void resolveAlarm(final long alarmId) throws IOException, NXCException
3068 {
3069 NXCPMessage msg = newMessage(NXCPCodes.CMD_RESOLVE_ALARM);
3070 msg.setFieldInt32(NXCPCodes.VID_ALARM_ID, (int)alarmId);
3071 sendMessage(msg);
3072 waitForRCC(msg.getMessageId());
3073 }
3074
3075 /**
3076 * Resolve alarm by helpdesk reference.
3077 *
3078 * @param helpdeskReference Identifier of alarm to be resolved.
3079 * @throws IOException if socket I/O error occurs
3080 * @throws NXCException if NetXMS server returns an error or operation was timed out
3081 */
3082 public void resolveAlarm(final String helpdeskReference) throws IOException, NXCException
3083 {
3084 NXCPMessage msg = newMessage(NXCPCodes.CMD_RESOLVE_ALARM);
3085 msg.setField(NXCPCodes.VID_HELPDESK_REF, helpdeskReference);
3086 sendMessage(msg);
3087 waitForRCC(msg.getMessageId());
3088 }
3089
3090 /**
3091 * Terminate alarm.
3092 *
3093 * @param alarmId Identifier of alarm to be terminated.
3094 * @throws IOException if socket I/O error occurs
3095 * @throws NXCException if NetXMS server returns an error or operation was timed out
3096 */
3097 public void terminateAlarm(final long alarmId) throws IOException, NXCException
3098 {
3099 NXCPMessage msg = newMessage(NXCPCodes.CMD_TERMINATE_ALARM);
3100 msg.setFieldInt32(NXCPCodes.VID_ALARM_ID, (int)alarmId);
3101 sendMessage(msg);
3102 waitForRCC(msg.getMessageId());
3103 }
3104
3105 /**
3106 * Terminate alarm by helpdesk reference.
3107 *
3108 * @param helpdeskReference Identifier of alarm to be resolved.
3109 * @throws IOException if socket I/O error occurs
3110 * @throws NXCException if NetXMS server returns an error or operation was timed out
3111 */
3112 public void terminateAlarm(final String helpdeskReference) throws IOException, NXCException
3113 {
3114 NXCPMessage msg = newMessage(NXCPCodes.CMD_TERMINATE_ALARM);
3115 msg.setField(NXCPCodes.VID_HELPDESK_REF, helpdeskReference);
3116 sendMessage(msg);
3117 waitForRCC(msg.getMessageId());
3118 }
3119
3120 private Map<Long, Integer> bulkAlarmOperation(int cmd, List<Long> alarmIds) throws IOException, NXCException
3121 {
3122 NXCPMessage msg = newMessage(cmd);
3123 msg.setField(NXCPCodes.VID_ALARM_ID_LIST, alarmIds.toArray(new Long[alarmIds.size()]));
3124 sendMessage(msg);
3125
3126 final NXCPMessage response = waitForRCC(msg.getMessageId());
3127
3128 Map<Long, Integer> operationFails = new HashMap<Long, Integer>();
3129
3130 // Returned alarm ID`s if there were any failed operations
3131 if (response.findField(NXCPCodes.VID_ALARM_ID_LIST) != null)
3132 {
3133 for(int i = 0; i < response.getFieldAsUInt32ArrayEx(NXCPCodes.VID_ALARM_ID_LIST).length; i++)
3134 {
3135 operationFails.put(response.getFieldAsUInt32ArrayEx(NXCPCodes.VID_ALARM_ID_LIST)[i],
3136 (Integer)response.getFieldAsUInt32ArrayEx(NXCPCodes.VID_FAIL_CODE_LIST)[i].intValue());
3137 }
3138 }
3139
3140
3141 return operationFails;
3142 }
3143
3144 /**
3145 * Bulk terminate alarms.
3146 *
3147 * @param alarmIds Identifiers of alarms to be terminated.
3148 * @throws IOException if socket I/O error occurs
3149 * @throws NXCException if NetXMS server returns an error or operation was timed out
3150 * @return true if all alarms were terminated, false if some, or all, were not terminated
3151 */
3152 public Map<Long, Integer> bulkResolveAlarms(List<Long> alarmIds) throws IOException, NXCException
3153 {
3154 return bulkAlarmOperation(NXCPCodes.CMD_BULK_RESOLVE_ALARMS, alarmIds);
3155 }
3156
3157 /**
3158 * Bulk terminate alarms.
3159 *
3160 * @param alarmIds Identifiers of alarms to be terminated.
3161 * @throws IOException if socket I/O error occurs
3162 * @throws NXCException if NetXMS server returns an error or operation was timed out
3163 * @return true if all alarms were terminated, false if some, or all, were not terminated
3164 */
3165 public Map<Long, Integer> bulkTerminateAlarms(List<Long> alarmIds) throws IOException, NXCException
3166 {
3167 return bulkAlarmOperation(NXCPCodes.CMD_BULK_TERMINATE_ALARMS, alarmIds);
3168 }
3169
3170 /**
3171 * Delete alarm.
3172 *
3173 * @param alarmId Identifier of alarm to be deleted.
3174 * @throws IOException if socket I/O error occurs
3175 * @throws NXCException if NetXMS server returns an error or operation was timed out
3176 */
3177 public void deleteAlarm(final