Java tutorial
/** * Copyright 2015 Smart Society Services B.V. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 */ package com.alliander.osgp.adapter.protocol.oslp.elster.infra.networking; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.lang.model.UnknownEntityException; import org.apache.commons.codec.binary.Base64; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.MessageEvent; import org.joda.time.Instant; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import com.alliander.osgp.adapter.protocol.oslp.elster.application.services.DeviceManagementService; import com.alliander.osgp.adapter.protocol.oslp.elster.application.services.DeviceRegistrationService; import com.alliander.osgp.adapter.protocol.oslp.elster.application.services.oslp.OslpDeviceSettingsService; import com.alliander.osgp.adapter.protocol.oslp.elster.application.services.oslp.OslpSigningService; import com.alliander.osgp.adapter.protocol.oslp.elster.domain.entities.OslpDevice; import com.alliander.osgp.adapter.protocol.oslp.elster.exceptions.ProtocolAdapterException; import com.alliander.osgp.core.db.api.application.services.DeviceDataService; import com.alliander.osgp.dto.valueobjects.GpsCoordinatesDto; import com.alliander.osgp.oslp.Oslp; import com.alliander.osgp.oslp.Oslp.EventNotification; import com.alliander.osgp.oslp.Oslp.EventNotificationRequest; import com.alliander.osgp.oslp.Oslp.LocationInfo; import com.alliander.osgp.oslp.Oslp.Message; import com.alliander.osgp.oslp.OslpEnvelope; import com.alliander.osgp.oslp.SignedOslpEnvelopeDto; public class OslpChannelHandlerServer extends OslpChannelHandler { private static final Logger LOGGER = LoggerFactory.getLogger(OslpChannelHandlerServer.class); private static DateTimeFormatter format = DateTimeFormat.forPattern("yyyyMMddHHmmss"); @Autowired private DeviceRegistrationService deviceRegistrationService; @Autowired private DeviceManagementService deviceManagementService; @Autowired private Integer sequenceNumberWindow; @Autowired private Integer timeZoneOffsetMinutes; @Autowired private OslpDeviceSettingsService oslpDeviceSettingsService; @Autowired private DeviceDataService deviceDataService; @Autowired private OslpSigningService oslpSigningService; private final ConcurrentMap<Integer, Channel> channelMap = new ConcurrentHashMap<>(); public OslpChannelHandlerServer() { super(LOGGER); } private Channel findChannel(final Integer channelId) { return this.channelMap.get(channelId); } private void cacheChannel(final Integer channelId, final Channel channel) { this.channelMap.put(channelId, channel); } public void setDeviceManagementService(final DeviceManagementService deviceManagementService) { this.deviceManagementService = deviceManagementService; } public void setDeviceRegistrationService(final DeviceRegistrationService deviceRegistrationService) { this.deviceRegistrationService = deviceRegistrationService; } public void setSequenceNumberWindow(final Integer sequenceNumberWindow) { this.sequenceNumberWindow = sequenceNumberWindow; } public void setTimeZoneOffsetMinutes(final int timeZoneOffsetMinutes) { this.timeZoneOffsetMinutes = timeZoneOffsetMinutes; } @Override public void messageReceived(final ChannelHandlerContext ctx, final MessageEvent e) throws Exception { final OslpEnvelope message = (OslpEnvelope) e.getMessage(); this.logMessage(message, true); final Integer channelId = e.getChannel().getId(); if (message.isValid()) { if (this.isOslpResponse(message)) { LOGGER.warn("{} Received OSLP Response, which is not expected: {}", channelId, message.getPayloadMessage()); } else { LOGGER.info("{} Received OSLP Request: {}", channelId, message.getPayloadMessage()); // Response pay-load to send to device. Message payload = null; // Check which request the device has sent and handle it. if (message.getPayloadMessage().hasRegisterDeviceRequest()) { payload = this.handleRegisterDeviceRequest(message.getDeviceId(), message.getSequenceNumber(), message.getPayloadMessage().getRegisterDeviceRequest()); } else if (message.getPayloadMessage().hasConfirmRegisterDeviceRequest()) { payload = this.handleConfirmRegisterDeviceRequest(message.getDeviceId(), message.getSequenceNumber(), message.getPayloadMessage().getConfirmRegisterDeviceRequest()); } else if (message.getPayloadMessage().hasEventNotificationRequest()) { payload = (this.handleEventNotificationRequest(message.getDeviceId(), message.getSequenceNumber(), message.getPayloadMessage().getEventNotificationRequest())); } else { LOGGER.warn("{} Received unknown payload. Received: {}.", channelId, message.getPayloadMessage().toString()); // TODO return error code to device. return; } // Cache the channel so we can write the response to it later. this.cacheChannel(channelId, e.getChannel()); // Send message to signing server to get our response signed. this.oslpSigningService.buildAndSignEnvelope(message.getDeviceId(), message.getSequenceNumber(), payload, channelId, this); } } else { LOGGER.warn("{} Received message wasn't properly secured.", channelId); } } /** * Called when a signed OSLP envelope arrives from signing server. The * envelope will be sent to the device which is waiting for a response. The * channel for the waiting device should be present in the channelMap. * * @param signedOslpEnvelopeDto * DTO containing signed OslpEnvelope. */ public void processSignedOslpEnvelope(final SignedOslpEnvelopeDto signedOslpEnvelopeDto) { // Try to find the channel. final Integer channelId = Integer .parseInt(signedOslpEnvelopeDto.getUnsignedOslpEnvelopeDto().getCorrelationUid()); final Channel channel = this.findChannel(channelId); if (channel == null) { LOGGER.error("Unable to find channel for channelId: {}. Can't send response message to device.", channelId); return; } // Get signed envelope, log it and send it to device. final OslpEnvelope response = signedOslpEnvelopeDto.getOslpEnvelope(); this.logMessage(response, false); channel.write(response); LOGGER.info("{} Send OSLP Response: {}", channelId, response.getPayloadMessage()); } private Oslp.Message handleRegisterDeviceRequest(final byte[] deviceUid, final byte[] sequenceNumber, final Oslp.RegisterDeviceRequest registerRequest) throws UnknownHostException { final InetAddress inetAddress = InetAddress.getByAddress(registerRequest.getIpAddress().toByteArray()); final String deviceType = registerRequest.getDeviceType().toString(); final boolean hasSchedule = registerRequest.getHasSchedule(); final String deviceIdentification = registerRequest.getDeviceIdentification(); // Send message to OSGP-CORE to save IP Address, device type and has // schedule values in OSGP-CORE database. this.deviceRegistrationService.sendDeviceRegisterRequest(inetAddress, deviceType, hasSchedule, deviceIdentification); OslpDevice oslpDevice = this.oslpDeviceSettingsService .getDeviceByDeviceIdentification(registerRequest.getDeviceIdentification()); // Save the security related values in the OSLP database. oslpDevice.updateRegistrationData(deviceUid, registerRequest.getDeviceType().toString(), Integer.valueOf(registerRequest.getRandomDevice())); oslpDevice.setSequenceNumber(SequenceNumberUtils.convertByteArrayToInteger(sequenceNumber)); oslpDevice = this.oslpDeviceSettingsService.updateDevice(oslpDevice); // Return current date and time in UTC so the device can sync the clock. final Oslp.RegisterDeviceResponse.Builder responseBuilder = Oslp.RegisterDeviceResponse.newBuilder() .setStatus(Oslp.Status.OK).setCurrentTime(Instant.now().toString(format)) .setRandomDevice(registerRequest.getRandomDevice()) .setRandomPlatform(oslpDevice.getRandomPlatform()); // Return local time zone information of the platform. Devices can use // this to convert UTC times to local times. final LocationInfo.Builder locationInfo = LocationInfo.newBuilder(); locationInfo.setTimeOffset(this.timeZoneOffsetMinutes); // Get the GPS values from OSGP-CORE database. final GpsCoordinatesDto gpsCoordinates = this.deviceDataService .getGpsCoordinatesForDevice(deviceIdentification); // Add GPS information when available in meta data. if (gpsCoordinates != null && gpsCoordinates.getLatitude() != null && gpsCoordinates.getLongitude() != null) { final int latitude = (int) ((gpsCoordinates.getLatitude()) * 1000000); final int longitude = (int) ((gpsCoordinates.getLongitude()) * 1000000); locationInfo.setLatitude(latitude).setLongitude(longitude); } responseBuilder.setLocationInfo(locationInfo); return Oslp.Message.newBuilder().setRegisterDeviceResponse(responseBuilder.build()).build(); } private Oslp.Message handleConfirmRegisterDeviceRequest(final byte[] deviceId, final byte[] sequenceNumber, final Oslp.ConfirmRegisterDeviceRequest confirmRegisterDeviceRequest) throws ProtocolAdapterException { try { this.deviceRegistrationService.confirmRegisterDevice(deviceId, SequenceNumberUtils.convertByteArrayToInteger(sequenceNumber), confirmRegisterDeviceRequest.getRandomDevice(), confirmRegisterDeviceRequest.getRandomPlatform()); } catch (final Exception e) { LOGGER.error("handle confirm register device request exception", e); throw new ProtocolAdapterException("ConfirmRegisterDevice failed", e); } return Oslp.Message.newBuilder() .setConfirmRegisterDeviceResponse(Oslp.ConfirmRegisterDeviceResponse.newBuilder() .setStatus(Oslp.Status.OK).setRandomDevice(confirmRegisterDeviceRequest.getRandomDevice()) .setRandomPlatform(confirmRegisterDeviceRequest.getRandomPlatform()) .setSequenceWindow(this.sequenceNumberWindow)) .build(); } private Oslp.Message handleEventNotificationRequest(final byte[] deviceId, final byte[] sequenceNumber, final EventNotificationRequest request) throws ProtocolAdapterException { // Check & update sequence number first try { this.deviceRegistrationService.updateDeviceSequenceNumber(deviceId, SequenceNumberUtils.convertByteArrayToInteger(sequenceNumber)); } catch (final ProtocolAdapterException ex) { LOGGER.error("handle event notification request exception", ex); return Oslp.Message.newBuilder().setEventNotificationResponse( Oslp.EventNotificationResponse.newBuilder().setStatus(Oslp.Status.REJECTED)).build(); } // Send event notifications to osgp core Oslp.Status oslpStatus = Oslp.Status.OK; for (final EventNotification event : request.getNotificationsList()) { Integer index = null; if (!event.getIndex().isEmpty()) { index = (int) event.getIndex().byteAt(0); } try { // Send the event notification to OSGP-CORE to save in the // database. this.deviceManagementService.addEventNotification(Base64.encodeBase64String(deviceId), event.getEvent().name(), event.getDescription(), index); } catch (final UnknownEntityException ex) { LOGGER.error("handle event notification request exception", ex); oslpStatus = Oslp.Status.REJECTED; } } return Oslp.Message.newBuilder() .setEventNotificationResponse(Oslp.EventNotificationResponse.newBuilder().setStatus(oslpStatus)) .build(); } }