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