Commit edc16ec4343a7abe5f9c89fc0a5e08f32bfbb277

Authored by Lawrence
1 parent 56996e21

修复收到Message Resquest后不回复200 OK的错误

src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/MessageRequestProcessor.java
... ... @@ -35,51 +35,51 @@ import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
35 35 import com.genersoft.iot.vmp.utils.redis.RedisUtil;
36 36 import org.springframework.util.StringUtils;
37 37  
38   -/**
  38 +/**
39 39 * @Description:MESSAGE请求处理器
40 40 * @author: swwheihei
41   - * @date: 2020年5月3日 下午5:32:41
  41 + * @date: 2020年5月3日 下午5:32:41
42 42 */
43 43 public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
44   -
  44 +
45 45 private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);
46   -
  46 +
47 47 private SIPCommander cmder;
48   -
  48 +
49 49 private IVideoManagerStorager storager;
50   -
  50 +
51 51 private EventPublisher publisher;
52   -
  52 +
53 53 private RedisUtil redis;
54   -
  54 +
55 55 private DeferredResultHolder deferredResultHolder;
56   -
  56 +
57 57 private DeviceOffLineDetector offLineDetector;
58   -
  58 +
59 59 private final static String CACHE_RECORDINFO_KEY = "CACHE_RECORDINFO_";
60   -
  60 +
61 61 private static final String MESSAGE_KEEP_ALIVE = "Keepalive";
62 62 private static final String MESSAGE_CONFIG_DOWNLOAD = "ConfigDownload";
63 63 private static final String MESSAGE_CATALOG = "Catalog";
64 64 private static final String MESSAGE_DEVICE_INFO = "DeviceInfo";
65 65 private static final String MESSAGE_ALARM = "Alarm";
66 66 private static final String MESSAGE_RECORD_INFO = "RecordInfo";
67   -// private static final String MESSAGE_BROADCAST = "Broadcast";
68   -// private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus";
69   -// private static final String MESSAGE_MOBILE_POSITION = "MobilePosition";
70   -// private static final String MESSAGE_MOBILE_POSITION_INTERVAL = "Interval";
71   -
72   - /**
  67 + // private static final String MESSAGE_BROADCAST = "Broadcast";
  68 + // private static final String MESSAGE_DEVICE_STATUS = "DeviceStatus";
  69 + // private static final String MESSAGE_MOBILE_POSITION = "MobilePosition";
  70 + // private static final String MESSAGE_MOBILE_POSITION_INTERVAL = "Interval";
  71 +
  72 + /**
73 73 * 处理MESSAGE请求
74   - *
  74 + *
75 75 * @param evt
76   - */
  76 + */
77 77 @Override
78 78 public void process(RequestEvent evt) {
79 79  
80 80 try {
81 81 Element rootElement = getRootElement(evt);
82   - String cmd = XmlUtil.getText(rootElement,"CmdType");
  82 + String cmd = XmlUtil.getText(rootElement, "CmdType");
83 83  
84 84 if (MESSAGE_KEEP_ALIVE.equals(cmd)) {
85 85 logger.info("接收到KeepAlive消息");
... ... @@ -103,9 +103,10 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
103 103 e.printStackTrace();
104 104 }
105 105 }
106   -
  106 +
107 107 /**
108 108 * 收到deviceInfo设备信息请求 处理
  109 + *
109 110 * @param evt
110 111 */
111 112 private void processMessageDeviceInfo(RequestEvent evt) {
... ... @@ -113,32 +114,38 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
113 114 Element rootElement = getRootElement(evt);
114 115 Element deviceIdElement = rootElement.element("DeviceID");
115 116 String deviceId = deviceIdElement.getText().toString();
116   -
  117 +
117 118 Device device = storager.queryVideoDevice(deviceId);
118 119 if (device == null) {
119 120 return;
120 121 }
121   - device.setName(XmlUtil.getText(rootElement,"DeviceName"));
122   - device.setManufacturer(XmlUtil.getText(rootElement,"Manufacturer"));
123   - device.setModel(XmlUtil.getText(rootElement,"Model"));
124   - device.setFirmware(XmlUtil.getText(rootElement,"Firmware"));
125   - if (StringUtils.isEmpty(device.getStreamMode())){
  122 + device.setName(XmlUtil.getText(rootElement, "DeviceName"));
  123 + device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer"));
  124 + device.setModel(XmlUtil.getText(rootElement, "Model"));
  125 + device.setFirmware(XmlUtil.getText(rootElement, "Firmware"));
  126 + if (StringUtils.isEmpty(device.getStreamMode())) {
126 127 device.setStreamMode("UDP");
127 128 }
128 129 storager.updateDevice(device);
129   -
  130 +
130 131 RequestMessage msg = new RequestMessage();
131 132 msg.setDeviceId(deviceId);
132 133 msg.setType(DeferredResultHolder.CALLBACK_CMD_DEVICEINFO);
133 134 msg.setData(device);
134 135 deferredResultHolder.invokeResult(msg);
135   - } catch (DocumentException e) {
  136 + // 回复200 OK
  137 + responseAck(evt);
  138 + if (offLineDetector.isOnline(deviceId)) {
  139 + publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
  140 + }
  141 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
136 142 e.printStackTrace();
137 143 }
138 144 }
139   -
  145 +
140 146 /***
141 147 * 收到catalog设备目录列表请求 处理
  148 + *
142 149 * @param evt
143 150 */
144 151 private void processMessageCatalogList(RequestEvent evt) {
... ... @@ -171,34 +178,43 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
171 178 DeviceChannel deviceChannel = new DeviceChannel();
172 179 deviceChannel.setName(channelName);
173 180 deviceChannel.setChannelId(channelDeviceId);
174   - if(status.equals("ON") || status.equals("On")) {
  181 + if (status.equals("ON") || status.equals("On")) {
175 182 deviceChannel.setStatus(1);
176 183 }
177   - if(status.equals("OFF") || status.equals("Off")) {
  184 + if (status.equals("OFF") || status.equals("Off")) {
178 185 deviceChannel.setStatus(0);
179 186 }
180 187  
181   - deviceChannel.setManufacture(XmlUtil.getText(itemDevice,"Manufacturer"));
182   - deviceChannel.setModel(XmlUtil.getText(itemDevice,"Model"));
183   - deviceChannel.setOwner(XmlUtil.getText(itemDevice,"Owner"));
184   - deviceChannel.setCivilCode(XmlUtil.getText(itemDevice,"CivilCode"));
185   - deviceChannel.setBlock(XmlUtil.getText(itemDevice,"Block"));
186   - deviceChannel.setAddress(XmlUtil.getText(itemDevice,"Address"));
187   - deviceChannel.setParental(itemDevice.element("Parental") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"Parental")));
188   - deviceChannel.setParentId(XmlUtil.getText(itemDevice,"ParentID"));
189   - deviceChannel.setSafetyWay(itemDevice.element("SafetyWay") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"SafetyWay")));
190   - deviceChannel.setRegisterWay(itemDevice.element("RegisterWay") == null? 1:Integer.parseInt(XmlUtil.getText(itemDevice,"RegisterWay")));
191   - deviceChannel.setCertNum(XmlUtil.getText(itemDevice,"CertNum"));
192   - deviceChannel.setCertifiable(itemDevice.element("Certifiable") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"Certifiable")));
193   - deviceChannel.setErrCode(itemDevice.element("ErrCode") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"ErrCode")));
194   - deviceChannel.setEndTime(XmlUtil.getText(itemDevice,"EndTime"));
195   - deviceChannel.setSecrecy(XmlUtil.getText(itemDevice,"Secrecy"));
196   - deviceChannel.setIpAddress(XmlUtil.getText(itemDevice,"IPAddress"));
197   - deviceChannel.setPort(itemDevice.element("Port") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"Port")));
198   - deviceChannel.setPassword(XmlUtil.getText(itemDevice,"Password"));
199   - deviceChannel.setLongitude(itemDevice.element("Longitude") == null? 0.00:Double.parseDouble(XmlUtil.getText(itemDevice,"Longitude")));
200   - deviceChannel.setLatitude(itemDevice.element("Latitude") == null? 0.00:Double.parseDouble(XmlUtil.getText(itemDevice,"Latitude")));
201   - deviceChannel.setPTZType(itemDevice.element("PTZType") == null? 0:Integer.parseInt(XmlUtil.getText(itemDevice,"PTZType")));
  188 + deviceChannel.setManufacture(XmlUtil.getText(itemDevice, "Manufacturer"));
  189 + deviceChannel.setModel(XmlUtil.getText(itemDevice, "Model"));
  190 + deviceChannel.setOwner(XmlUtil.getText(itemDevice, "Owner"));
  191 + deviceChannel.setCivilCode(XmlUtil.getText(itemDevice, "CivilCode"));
  192 + deviceChannel.setBlock(XmlUtil.getText(itemDevice, "Block"));
  193 + deviceChannel.setAddress(XmlUtil.getText(itemDevice, "Address"));
  194 + deviceChannel.setParental(itemDevice.element("Parental") == null ? 0
  195 + : Integer.parseInt(XmlUtil.getText(itemDevice, "Parental")));
  196 + deviceChannel.setParentId(XmlUtil.getText(itemDevice, "ParentID"));
  197 + deviceChannel.setSafetyWay(itemDevice.element("SafetyWay") == null ? 0
  198 + : Integer.parseInt(XmlUtil.getText(itemDevice, "SafetyWay")));
  199 + deviceChannel.setRegisterWay(itemDevice.element("RegisterWay") == null ? 1
  200 + : Integer.parseInt(XmlUtil.getText(itemDevice, "RegisterWay")));
  201 + deviceChannel.setCertNum(XmlUtil.getText(itemDevice, "CertNum"));
  202 + deviceChannel.setCertifiable(itemDevice.element("Certifiable") == null ? 0
  203 + : Integer.parseInt(XmlUtil.getText(itemDevice, "Certifiable")));
  204 + deviceChannel.setErrCode(itemDevice.element("ErrCode") == null ? 0
  205 + : Integer.parseInt(XmlUtil.getText(itemDevice, "ErrCode")));
  206 + deviceChannel.setEndTime(XmlUtil.getText(itemDevice, "EndTime"));
  207 + deviceChannel.setSecrecy(XmlUtil.getText(itemDevice, "Secrecy"));
  208 + deviceChannel.setIpAddress(XmlUtil.getText(itemDevice, "IPAddress"));
  209 + deviceChannel.setPort(itemDevice.element("Port") == null ? 0
  210 + : Integer.parseInt(XmlUtil.getText(itemDevice, "Port")));
  211 + deviceChannel.setPassword(XmlUtil.getText(itemDevice, "Password"));
  212 + deviceChannel.setLongitude(itemDevice.element("Longitude") == null ? 0.00
  213 + : Double.parseDouble(XmlUtil.getText(itemDevice, "Longitude")));
  214 + deviceChannel.setLatitude(itemDevice.element("Latitude") == null ? 0.00
  215 + : Double.parseDouble(XmlUtil.getText(itemDevice, "Latitude")));
  216 + deviceChannel.setPTZType(itemDevice.element("PTZType") == null ? 0
  217 + : Integer.parseInt(XmlUtil.getText(itemDevice, "PTZType")));
202 218 deviceChannel.setHasAudio(true); // 默认含有音频,播放时再检查是否有音频及是否AAC
203 219 storager.updateChannel(device.getDeviceId(), deviceChannel);
204 220 }
... ... @@ -208,21 +224,20 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
208 224 msg.setType(DeferredResultHolder.CALLBACK_CMD_CATALOG);
209 225 msg.setData(device);
210 226 deferredResultHolder.invokeResult(msg);
211   -
212   - // 回复200
  227 + // 回复200 OK
  228 + responseAck(evt);
213 229 if (offLineDetector.isOnline(deviceId)) {
214   - responseAck(evt);
215 230 publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
216 231 }
217   -
218 232 }
219 233 } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
220 234 e.printStackTrace();
221 235 }
222 236 }
223   -
  237 +
224 238 /***
225 239 * 收到alarm设备报警信息 处理
  240 + *
226 241 * @param evt
227 242 */
228 243 private void processMessageAlarm(RequestEvent evt) {
... ... @@ -230,37 +245,45 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
230 245 Element rootElement = getRootElement(evt);
231 246 Element deviceIdElement = rootElement.element("DeviceID");
232 247 String deviceId = deviceIdElement.getText().toString();
233   -
  248 +
234 249 Device device = storager.queryVideoDevice(deviceId);
235 250 if (device == null) {
236 251 // TODO 也可能是通道
237   -// storager.queryChannel(deviceId)
  252 + // storager.queryChannel(deviceId)
238 253 return;
239 254 }
240   - device.setName(XmlUtil.getText(rootElement,"DeviceName"));
241   - device.setManufacturer(XmlUtil.getText(rootElement,"Manufacturer"));
242   - device.setModel(XmlUtil.getText(rootElement,"Model"));
243   - device.setFirmware(XmlUtil.getText(rootElement,"Firmware"));
244   - if (StringUtils.isEmpty(device.getStreamMode())){
  255 + device.setName(XmlUtil.getText(rootElement, "DeviceName"));
  256 + device.setManufacturer(XmlUtil.getText(rootElement, "Manufacturer"));
  257 + device.setModel(XmlUtil.getText(rootElement, "Model"));
  258 + device.setFirmware(XmlUtil.getText(rootElement, "Firmware"));
  259 + if (StringUtils.isEmpty(device.getStreamMode())) {
245 260 device.setStreamMode("UDP");
246 261 }
247 262 storager.updateDevice(device);
248 263 cmder.catalogQuery(device);
249   - } catch (DocumentException e) {
  264 + // 回复200 OK
  265 + responseAck(evt);
  266 + if (offLineDetector.isOnline(deviceId)) {
  267 + publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
  268 + }
  269 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
  270 + // } catch (DocumentException e) {
250 271 e.printStackTrace();
251 272 }
252 273 }
253   -
  274 +
254 275 /***
255 276 * 收到keepalive请求 处理
  277 + *
256 278 * @param evt
257 279 */
258   - private void processMessageKeepAlive(RequestEvent evt){
  280 + private void processMessageKeepAlive(RequestEvent evt) {
259 281 try {
260 282 Element rootElement = getRootElement(evt);
261   - String deviceId = XmlUtil.getText(rootElement,"DeviceID");
  283 + String deviceId = XmlUtil.getText(rootElement, "DeviceID");
  284 + // 回复200 OK
  285 + responseAck(evt);
262 286 if (offLineDetector.isOnline(deviceId)) {
263   - responseAck(evt);
264 287 publisher.onlineEventPublish(deviceId, VideoManagerConstants.EVENT_ONLINE_KEEPLIVE);
265 288 } else {
266 289 }
... ... @@ -268,58 +291,66 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
268 291 e.printStackTrace();
269 292 }
270 293 }
271   -
  294 +
272 295 /***
273   - * 收到catalog设备目录列表请求 处理
274   - * TODO 过期时间暂时写死180秒,后续与DeferredResult超时时间保持一致
  296 + * 收到catalog设备目录列表请求 处理 TODO 过期时间暂时写死180秒,后续与DeferredResult超时时间保持一致
  297 + *
275 298 * @param evt
276 299 */
277 300 private void processMessageRecordInfo(RequestEvent evt) {
278 301 try {
  302 + // 回复200 OK
  303 + responseAck(evt);
279 304 RecordInfo recordInfo = new RecordInfo();
280 305 Element rootElement = getRootElement(evt);
281 306 Element deviceIdElement = rootElement.element("DeviceID");
282 307 String deviceId = deviceIdElement.getText().toString();
283 308 recordInfo.setDeviceId(deviceId);
284   - recordInfo.setName(XmlUtil.getText(rootElement,"Name"));
285   - recordInfo.setSumNum(Integer.parseInt(XmlUtil.getText(rootElement,"SumNum")));
286   - String sn = XmlUtil.getText(rootElement,"SN");
  309 + recordInfo.setName(XmlUtil.getText(rootElement, "Name"));
  310 + recordInfo.setSumNum(Integer.parseInt(XmlUtil.getText(rootElement, "SumNum")));
  311 + String sn = XmlUtil.getText(rootElement, "SN");
287 312 Element recordListElement = rootElement.element("RecordList");
288 313 if (recordListElement == null) {
  314 + logger.info("无录像数据");
  315 + // responseAck(evt);
289 316 return;
290 317 }
291   -
  318 +
292 319 Iterator<Element> recordListIterator = recordListElement.elementIterator();
293 320 List<RecordItem> recordList = new ArrayList<RecordItem>();
294 321 if (recordListIterator != null) {
295 322 RecordItem record = new RecordItem();
  323 + logger.info("处理录像列表数据...");
296 324 // 遍历DeviceList
297 325 while (recordListIterator.hasNext()) {
298 326 Element itemRecord = recordListIterator.next();
299 327 Element recordElement = itemRecord.element("DeviceID");
300 328 if (recordElement == null) {
  329 + logger.info("记录为空,下一个...");
301 330 continue;
302 331 }
303 332 record = new RecordItem();
304   - record.setDeviceId(XmlUtil.getText(itemRecord,"DeviceID"));
305   - record.setName(XmlUtil.getText(itemRecord,"Name"));
306   - record.setFilePath(XmlUtil.getText(itemRecord,"FilePath"));
307   - record.setAddress(XmlUtil.getText(itemRecord,"Address"));
308   - record.setStartTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord,"StartTime")));
309   - record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord,"EndTime")));
310   - record.setSecrecy(itemRecord.element("Secrecy") == null? 0:Integer.parseInt(XmlUtil.getText(itemRecord,"Secrecy")));
311   - record.setType(XmlUtil.getText(itemRecord,"Type"));
312   - record.setRecorderId(XmlUtil.getText(itemRecord,"RecorderID"));
  333 + record.setDeviceId(XmlUtil.getText(itemRecord, "DeviceID"));
  334 + record.setName(XmlUtil.getText(itemRecord, "Name"));
  335 + record.setFilePath(XmlUtil.getText(itemRecord, "FilePath"));
  336 + record.setAddress(XmlUtil.getText(itemRecord, "Address"));
  337 + record.setStartTime(
  338 + DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord, "StartTime")));
  339 + record.setEndTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(XmlUtil.getText(itemRecord, "EndTime")));
  340 + record.setSecrecy(itemRecord.element("Secrecy") == null ? 0
  341 + : Integer.parseInt(XmlUtil.getText(itemRecord, "Secrecy")));
  342 + record.setType(XmlUtil.getText(itemRecord, "Type"));
  343 + record.setRecorderId(XmlUtil.getText(itemRecord, "RecorderID"));
313 344 recordList.add(record);
314 345 }
315   -// recordList.sort(Comparator.naturalOrder());
  346 + // recordList.sort(Comparator.naturalOrder());
316 347 recordInfo.setRecordList(recordList);
317 348 }
318   -
  349 +
319 350 // 存在录像且如果当前录像明细个数小于总条数,说明拆包返回,需要组装,暂不返回
320 351 if (recordInfo.getSumNum() > 0 && recordList.size() > 0 && recordList.size() < recordInfo.getSumNum()) {
321 352 // 为防止连续请求该设备的录像数据,返回数据错乱,特增加sn进行区分
322   - String cacheKey = CACHE_RECORDINFO_KEY+deviceId+sn;
  353 + String cacheKey = CACHE_RECORDINFO_KEY + deviceId + sn;
323 354 // TODO 暂时直接操作redis存储,后续封装专用缓存接口,改为本地内存缓存
324 355 if (redis.hasKey(cacheKey)) {
325 356 List<RecordItem> previousList = (List<RecordItem>) redis.get(cacheKey);
... ... @@ -328,24 +359,28 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
328 359 }
329 360 // 本分支表示录像列表被拆包,且加上之前的数据还是不够,保存缓存返回,等待下个包再处理
330 361 if (recordList.size() < recordInfo.getSumNum()) {
331   - redis.set(cacheKey, recordList, 180);
  362 + logger.info("已获取" + recordList.size() + "项录像数据,共" + recordInfo.getSumNum() + "项");
  363 + redis.set(cacheKey, recordList, 90);
332 364 return;
333 365 } else {
334 366 // 本分支表示录像被拆包,但加上之前的数据够足够,返回响应
335 367 // 因设备心跳有监听redis过期机制,为提高性能,此处手动删除
  368 + logger.info("录像数据已全部获取");
336 369 redis.del(cacheKey);
337 370 }
338 371 } else {
339 372 // 本分支有两种可能:1、录像列表被拆包,且是第一个包,直接保存缓存返回,等待下个包再处理
340   - // 2、之前有包,但超时清空了,那么这次sn批次的响应数据已经不完整,等待过期时间后redis自动清空数据
341   - redis.set(cacheKey, recordList, 180);
  373 + // 2、之前有包,但超时清空了,那么这次sn批次的响应数据已经不完整,等待过期时间后redis自动清空数据
  374 + logger.info("等待后续的包...");
  375 +
  376 + redis.set(cacheKey, recordList, 90);
342 377 return;
343 378 }
344   -
  379 +
345 380 }
346 381 // 走到这里,有以下可能:1、没有录像信息,第一次收到recordinfo的消息即返回响应数据,无redis操作
347   - // 2、有录像数据,且第一次即收到完整数据,返回响应数据,无redis操作
348   - // 3、有录像数据,在超时时间内收到多次包组装后数量足够,返回数据
  382 + // 2、有录像数据,且第一次即收到完整数据,返回响应数据,无redis操作
  383 + // 3、有录像数据,在超时时间内收到多次包组装后数量足够,返回数据
349 384  
350 385 // 对记录进行排序
351 386 RequestMessage msg = new RequestMessage();
... ... @@ -355,16 +390,17 @@ public class MessageRequestProcessor extends SIPRequestAbstractProcessor {
355 390 recordInfo.getRecordList().sort(Comparator.naturalOrder());
356 391 msg.setData(recordInfo);
357 392 deferredResultHolder.invokeResult(msg);
358   - } catch (DocumentException e) {
  393 + logger.info("处理完成,返回结果");
  394 + } catch (DocumentException | SipException | InvalidArgumentException | ParseException e) {
359 395 e.printStackTrace();
360 396 }
361 397 }
362   -
  398 +
363 399 private void responseAck(RequestEvent evt) throws SipException, InvalidArgumentException, ParseException {
364   - Response response = getMessageFactory().createResponse(Response.OK,evt.getRequest());
  400 + Response response = getMessageFactory().createResponse(Response.OK, evt.getRequest());
365 401 getServerTransaction(evt).sendResponse(response);
366 402 }
367   -
  403 +
368 404 private Element getRootElement(RequestEvent evt) throws DocumentException {
369 405 Request request = evt.getRequest();
370 406 SAXReader reader = new SAXReader();
... ...