/** * Constructor to set up connection parameters using the {@link DeviceClientConfig}. * * @param config The {@link DeviceClientConfig} corresponding to the device associated with this * {@link com.microsoft.azure.iothub.DeviceClient}. */ public AmqpsIotHubConnection(DeviceClientConfig config) { // Codes_SRS_AMQPSIOTHUBCONNECTION_14_002: [The constructor shall throw a new // IllegalArgumentException if any of the parameters of the configuration is null or empty.] if (config == null) { throw new IllegalArgumentException("The DeviceClientConfig cannot be null."); } if (config.getIotHubHostname() == null || config.getIotHubHostname().length() == 0) { throw new IllegalArgumentException("hostName cannot be null or empty."); } if (config.getDeviceId() == null || config.getDeviceId().length() == 0) { throw new IllegalArgumentException("deviceID cannot be null or empty."); } if (config.getIotHubName() == null || config.getIotHubName().length() == 0) { throw new IllegalArgumentException("hubName cannot be null or empty."); } if (config.getDeviceKey() == null || config.getDeviceKey().length() == 0) { throw new IllegalArgumentException("deviceKey cannot be null or empty."); } // Codes_SRS_AMQPSIOTHUBCONNECTION_14_001: [The constructor shall save the configuration.] this.config = config; String iotHubHostname = this.config.getIotHubHostname(); String iotHubName = this.config.getIotHubName(); String deviceId = this.config.getDeviceId(); String iotHubUser = deviceId + "@sas." + iotHubName; // Codes_SRS_AMQPSIOTHUBCONNECTION_14_003: [The constructor shall create a new SAS token and // copy all input parameters to private member variables.] IotHubSasToken sasToken = new IotHubSasToken(this.config); // Codes_SRS_AMQPSIOTHUBCONNECTION_14_006: [The constructor shall initialize a new private map // for messages that are in progress.] // Codes_SRS_AMQPSIOTHUBCONNECTION_14_007: [The constructor shall initialize new private Futures // for the status of the Connection and Reactor.] this.hostName = iotHubHostname; this.userName = iotHubUser; this.deviceID = deviceId; this.sasToken = sasToken.toString(); // Codes_SRS_AMQPSIOTHUBCONNECTION_14_004: [The constructor shall set it’s state to CLOSED.] this.state = ReactorState.CLOSED; // Codes_SRS_AMQPSIOTHUBCONNECTION_14_005: [The constructor shall initialize a new private queue // for received messages.] // Codes_SRS_AMQPSIOTHUBCONNECTION_14_006: [The constructor shall initialize a new private map // for messages that are in progress.] receivedMessageQueue = new LinkedBlockingQueue<>(); inProgressMessageMap = new HashMap<>(); this.maxQueueSize = -1; // Codes_SRS_AMQPSIOTHUBCONNECTION_14_007: [The constructor shall initialize new private Futures // for the status of the Connection and Reactor.] completionStatus = new CompletableFuture<>(); reactorReady = new CompletableFuture<>(); }
/** * Opens the {@link AmqpsIotHubConnection} creating a new {@link * AmqpsIotHubConnectionBaseHandler}. * * <p>If the current {@link AmqpsIotHubConnection.ReactorState} is not OPEN, this method will * create a new {@link IotHubSasToken} and use it to create a new {@link * AmqpsIotHubConnectionBaseHandler}. This method will start the {@link Reactor}, set the current * {@link AmqpsIotHubConnection.ReactorState} to OPEN, and open the {@link AmqpsIotHubConnection} * for sending. * * @throws IOException if the {@link AmqpsIotHubConnectionBaseHandler} has not been initialized. * @throws InterruptedException if there is a problem acquiring the semaphore for the {@link * Reactor}. * @throws ExecutionException If the {@link CompletableFuture} in {@link * AmqpsIotHubConnection#reactorReady()} completed exceptionally */ public void open() throws IOException, InterruptedException, ExecutionException { // Codes_SRS_AMQPSIOTHUBCONNECTION_14_011: [If the AMQPS connection is already open, the // function shall do nothing.] if (this.state != ReactorState.OPEN) { // Codes_SRS_AMQPSIOTHUBCONNECTION_14_008: [The function shall initialize it’s // AmqpsIotHubConnectionBaseHandler using the saved host name, user name, device ID and sas // token.] IotHubSasToken sasToken = new IotHubSasToken(this.config); this.amqpsHandler = new AmqpsIotHubConnectionBaseHandler( this.hostName, this.userName, sasToken.toString(), this.deviceID, this); // Codes_SRS_AMQPSIOTHUBCONNECTION_14_009: [The function shall open the Amqps connection and // trigger the Reactor (Proton) to begin running.] // Codes_SRS_AMQPSIOTHUBCONNECTION_14_012: [If the AmqpsIotHubConnectionBaseHandler becomes // invalidated before the Reactor (Proton) starts, the function shall throw an IOException.] this.startReactorAsync(); // Codes_SRS_AMQPSIOTHUBCONNECTION_14_010: [Once the Reactor (Proton) is ready, the function // shall set its state to OPEN.] // Codes_SRS_AMQPSIOTHUBCONNECTION_14_031: [ The function shall get the link credit from it's // AmqpsIotHubConnectionBaseHandler and set the private maxQueueSize member variable. ] // Codes_SRS_AMQPSIOTHUBCONNECTION_14_032: [ The function shall successfully complete it’s // CompletableFuture status member variable. ] try { this.reactorReady(); // This is a blocking call, will return once the link credit is available from the // AmqpsIotHubBaseHandler this.maxQueueSize = this.amqpsHandler.getLinkCredit(); } catch (TimeoutException e) { this.amqpsHandler.shutdown(); throw new ExecutionException( "The request to get the link credit from the AmqpsIotHubBaseHandler timed out.", e); } catch (Exception e) { this.amqpsHandler.shutdown(); throw e; } this.completionStatus.complete(new Boolean(true)); this.state = ReactorState.OPEN; } // TODO: Should this wrap all exceptions in an IOException and only throw that? }
// Tests_SRS_AMQPSIOTHUBSESSION_11_022: [The constructor shall save the configuration.] // Tests_SRS_AMQPSIOTHUBSESSION_11_001: [The function shall establish an AMQPS session with an IoT // Hub with the resource URI // 'amqps://[urlEncodedDeviceId]@sas.[urlEncodedIotHubName]:[urlEncodedSasToken]@[urlEncodedIotHubHostname].] @Test public void openEstablishesSessionUsingCorrectUri() throws JMSException, IOException { final String iotHubHostname = "test.iothub.testhub"; final String iotHubName = "test.iothub"; final String deviceId = "('test-deviceid')"; final String deviceKey = "test-devicekey?&test"; final String resourceUri = "test-resource-uri"; new NonStrictExpectations() { { mockConfig.getIotHubHostname(); result = iotHubHostname; mockConfig.getIotHubName(); result = iotHubName; mockConfig.getDeviceId(); result = deviceId; mockConfig.getDeviceKey(); result = deviceKey; IotHubUri.getResourceUri(iotHubHostname, deviceId); result = resourceUri; new IotHubSasToken(resourceUri, deviceId, deviceKey, anyLong); result = mockToken; } }; AmqpsIotHubSession session = new AmqpsIotHubSession(mockConfig); session.open(); final String expectedUsername = deviceId + "@sas." + iotHubName; final String expectedPassword = mockToken.toString(); final String expectedHostname = "amqps://" + iotHubHostname; new Verifications() { { new JmsConnectionFactory(expectedUsername, expectedPassword, expectedHostname); } }; }