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