From 65c617e8d932eb27243155e13e36f99c30ae3ca0 Mon Sep 17 00:00:00 2001
From: timtay-microsoft <timtay@microsoft.com>
Date: Tue, 11 Apr 2023 12:02:24 -0700
Subject: [PATCH] feat(dps-device): Add synchronous overloads for
 registerDevice

A lot of our samples and tests are simplified with this and a lot of customers would probably benefit the same way.
---
 .../setup/ProvisioningCommon.java             |  40 ++---
 .../azure/sdk/iot/ProvisioningTPMTests.java   |  51 ++----
 .../sdk/iot/device/TemperatureController.java |  36 +---
 .../azure/sdk/iot/device/Thermostat.java      |  59 +------
 .../azure/sdk/iot/ProvisioningX509Sample.java | 154 ++++--------------
 ...ningSymmetricKeyEnrollmentGroupSample.java | 137 +++-------------
 ...ymmetricKeyIndividualEnrollmentSample.java | 126 +++-----------
 .../azure/sdk/iot/ProvisioningTpmSample.java  | 115 +++----------
 .../device/ProvisioningDeviceClient.java      | 111 ++++++++++---
 9 files changed, 231 insertions(+), 598 deletions(-)

diff --git a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/provisioning/setup/ProvisioningCommon.java b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/provisioning/setup/ProvisioningCommon.java
index 2e918cf286..a3d2f262e0 100644
--- a/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/provisioning/setup/ProvisioningCommon.java
+++ b/iot-e2e-tests/common/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/provisioning/setup/ProvisioningCommon.java
@@ -223,41 +223,27 @@ public void registerDevice(ProvisioningDeviceClientTransportProtocol protocol, S
 
         try
         {
-            final CountDownLatch registrationLatch = new CountDownLatch(1);
-            AtomicReference<ProvisioningDeviceClientRegistrationResult> registrationResultReference = new AtomicReference<>();
-            AtomicReference<Exception> registrationExceptionReference = new AtomicReference<>();
-            provisioningDeviceClient.registerDevice(
-                (provisioningDeviceClientRegistrationResult, e, context) ->
-                {
-                    registrationResultReference.set(provisioningDeviceClientRegistrationResult);
-                    registrationExceptionReference.set(e);
-                    registrationLatch.countDown();
-                },
-                null);
-
-            // Wait until registration finishes or for a max amount of time
-            boolean timedOut = !registrationLatch.await(MAX_TIME_TO_WAIT_FOR_REGISTRATION_SECONDS, TimeUnit.SECONDS);
-            if (timedOut)
+            ProvisioningDeviceClientRegistrationResult registrationResult;
+            try
             {
-                fail("Timed out waiting for device registration to complete.");
+                registrationResult = provisioningDeviceClient.registerDeviceSync();
             }
-
-            ProvisioningDeviceClientRegistrationResult registrationResult = registrationResultReference.get();
-            Exception registrationException = registrationExceptionReference.get();
-
-            if (registrationException != null)
+            catch (Exception e)
             {
-                String errorContext = "";
-                errorContext += " Status=" + registrationResult.getStatus();
-                errorContext += " Substatus=" + registrationResult.getSubstatus();
-                if (registrationException instanceof ProvisioningDeviceClientException)
+                String errorContext = "Provisioning threw an exception.";
+                if (e instanceof ProvisioningDeviceClientException)
                 {
-                    errorContext += " Error code=" + ((ProvisioningDeviceHubException) registrationException).getErrorCode();
+                    errorContext += " Error code=" + ((ProvisioningDeviceHubException) e).getErrorCode();
                 }
 
                 // While this would normally throw an assertion failed exception, those exceptions do not allow for nesting
                 // of exception causes. The invalid server cert tests need to be able to check that inner exception, though.
-                throw new Exception("Registration finished with exception." + errorContext, registrationException);
+                throw new Exception("Registration finished with exception." + errorContext, e);
+            }
+
+            if (registrationResult.getProvisioningDeviceClientStatus() != PROVISIONING_DEVICE_STATUS_ASSIGNED)
+            {
+                fail("Provisioning finished with unsuccessful state. Status=" + registrationResult.getStatus() + " Substatus=" + registrationResult.getSubstatus());
             }
 
             Assert.assertEquals(CorrelationDetailsLoggingAssert.buildExceptionMessageDpsIndividualOrGroup("Unexpected status", Tools.getHostName(provisioningServiceConnectionString), testInstance.groupId, testInstance.registrationId), PROVISIONING_DEVICE_STATUS_ASSIGNED, registrationResult.getProvisioningDeviceClientStatus());
diff --git a/iot-e2e-tests/iot-e2e-jvm-tests/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/ProvisioningTPMTests.java b/iot-e2e-tests/iot-e2e-jvm-tests/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/ProvisioningTPMTests.java
index 105d3285ff..1bd1e645ad 100644
--- a/iot-e2e-tests/iot-e2e-jvm-tests/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/ProvisioningTPMTests.java
+++ b/iot-e2e-tests/iot-e2e-jvm-tests/src/test/java/tests/integration/com/microsoft/azure/sdk/iot/ProvisioningTPMTests.java
@@ -18,6 +18,7 @@
 import com.microsoft.azure.sdk.iot.provisioning.service.configs.IndividualEnrollment;
 import com.microsoft.azure.sdk.iot.provisioning.service.configs.TpmAttestation;
 import com.microsoft.azure.sdk.iot.provisioning.service.exceptions.ProvisioningServiceClientException;
+import junit.framework.AssertionFailedError;
 import lombok.extern.slf4j.Slf4j;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -28,6 +29,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
+import static com.microsoft.azure.sdk.iot.provisioning.device.ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED;
 import static com.microsoft.azure.sdk.iot.provisioning.service.configs.AttestationMechanismType.TPM;
 import static junit.framework.TestCase.*;
 import static org.apache.commons.codec.binary.Base64.encodeBase64;
@@ -67,46 +69,25 @@ public void provisioningTpmFlow() throws SecurityProviderException, Provisioning
                 ProvisioningDeviceClientTransportProtocol.AMQPS,
                 securityProvider);
 
-        final CountDownLatch registrationLatch = new CountDownLatch(1);
-        AtomicReference<ProvisioningDeviceClientRegistrationResult> registrationResultReference = new AtomicReference<>();
-        AtomicReference<Exception> registrationExceptionReference = new AtomicReference<>();
-        provisioningDeviceClient.registerDevice(
-            (provisioningDeviceClientRegistrationResult, e, context) ->
-            {
-                log.debug("Provisioning registration callback fired with result {}", provisioningDeviceClientRegistrationResult.getProvisioningDeviceClientStatus());
-
-                registrationResultReference.set(provisioningDeviceClientRegistrationResult);
-                if (e != null)
-                {
-                    registrationExceptionReference.set(e);
-                }
-
-                registrationLatch.countDown();
-            },
-            null);
-
-        boolean timedOut = !registrationLatch.await(REGISTRATION_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-        if (timedOut)
+        ProvisioningDeviceClientRegistrationResult registrationResult;
+        try
         {
-            fail("Timed out waiting for device registration to complete.");
+            registrationResult = provisioningDeviceClient.registerDeviceSync();
         }
-
-        provisioningDeviceClient.close();
-
-        ProvisioningDeviceClientRegistrationResult registrationResult = registrationResultReference.get();
-        Exception registrationException = registrationExceptionReference.get();
-        log.info("Registration completed with status {}", registrationResult.getStatus());
-
-        if (registrationException != null)
+        catch (Exception e)
         {
-            String errorContext = "";
-            errorContext += " Status=" + registrationResult.getStatus();
-            errorContext += " Substatus=" + registrationResult.getSubstatus();
-            if (registrationException instanceof ProvisioningDeviceClientException)
+            String errorContext = "Provisioning threw an exception.";
+            if (e instanceof ProvisioningDeviceClientException)
             {
-                errorContext += " Error code=" + ((ProvisioningDeviceHubException) registrationException).getErrorCode();
+                errorContext += " Error code=" + ((ProvisioningDeviceHubException) e).getErrorCode();
             }
-            fail("Registration finished with exception." + errorContext);
+
+            throw new AssertionFailedError("Registration finished with exception." + errorContext);
+        }
+
+        if (registrationResult.getProvisioningDeviceClientStatus() != PROVISIONING_DEVICE_STATUS_ASSIGNED)
+        {
+            fail("Provisioning finished with unsuccessful state. Status=" + registrationResult.getStatus() + " Substatus=" + registrationResult.getSubstatus());
         }
 
         assertEquals("Registration completed, but not successfully", registrationResult.getProvisioningDeviceClientStatus(), ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED);
diff --git a/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/device/TemperatureController.java b/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/device/TemperatureController.java
index 97658ebb07..2b7fc9868f 100644
--- a/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/device/TemperatureController.java
+++ b/iothub/device/iot-device-samples/pnp-device-sample/temperature-controller-device-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/device/TemperatureController.java
@@ -80,8 +80,6 @@ public enum StatusCode {
     private static final ProvisioningDeviceClientTransportProtocol provisioningProtocol = ProvisioningDeviceClientTransportProtocol.MQTT;
     private static final IotHubClientProtocol protocol = IotHubClientProtocol.MQTT;
 
-    private static final int MAX_TIME_TO_WAIT_FOR_REGISTRATION = 1000; // in milli seconds
-
     private static final Random random = new Random();
     private static final Gson gson = new Gson();
     private static DeviceClient deviceClient;
@@ -97,9 +95,8 @@ public enum StatusCode {
     // HashMap to hold the max temperature since last reboot, for each "Thermostat" component.
     private static final Map<String, Double> maxTemperature = new HashMap<>();
 
-    public static void main(String[] args) throws IOException, URISyntaxException, ProvisioningDeviceClientException, InterruptedException, IotHubClientException, TimeoutException
+    public static void main(String[] args) throws Exception
     {
-
         // This sample follows the following workflow:
         // -> Initialize device client instance.
         // -> Set handler to receive "reboot" command - root interface.
@@ -225,7 +222,7 @@ private static boolean validateArgsForDpsFlow()
                 && (deviceSymmetricKey == null || deviceSymmetricKey.isEmpty()));
     }
 
-    private static void initializeAndProvisionDevice() throws ProvisioningDeviceClientException, IOException, URISyntaxException, InterruptedException, IotHubClientException
+    private static void initializeAndProvisionDevice() throws Exception
     {
         SecurityProviderSymmetricKey securityClientSymmetricKey = new SecurityProviderSymmetricKey(deviceSymmetricKey.getBytes(StandardCharsets.UTF_8), registrationId);
         ProvisioningDeviceClient provisioningDeviceClient;
@@ -235,34 +232,7 @@ private static void initializeAndProvisionDevice() throws ProvisioningDeviceClie
         AdditionalData additionalData = new AdditionalData();
         additionalData.setProvisioningPayload(com.microsoft.azure.sdk.iot.provisioning.device.plugandplay.PnpHelper.createDpsPayload(MODEL_ID));
 
-        final CountDownLatch registrationLatch = new CountDownLatch(1);
-        AtomicReference<ProvisioningDeviceClientRegistrationResult> registrationResultReference = new AtomicReference<>();
-        AtomicReference<Exception> registrationExceptionReference = new AtomicReference<>();
-        provisioningDeviceClient.registerDevice(
-            (provisioningDeviceClientRegistrationResult, e, context) ->
-            {
-                registrationResultReference.set(provisioningDeviceClientRegistrationResult);
-                registrationExceptionReference.set(e);
-                registrationLatch.countDown();
-            },
-            null,
-            additionalData);
-
-        System.out.println("Waiting for Provisioning Service to register");
-        // Time out after 1 minute of waiting. Typically, this operation only takes a few seconds.
-        boolean timedOut = !registrationLatch.await(1, TimeUnit.MINUTES);
-        if (timedOut)
-        {
-            throw new IotHubClientException(IotHubStatusCode.DEVICE_OPERATION_TIMED_OUT, "Timed out waiting for provisioning to finish");
-        }
-
-        ProvisioningDeviceClientRegistrationResult registrationResult = registrationResultReference.get();
-        Exception registrationException = registrationExceptionReference.get();
-
-        if (registrationException != null)
-        {
-            throw new IotHubClientException(IotHubStatusCode.ERROR, "Provisioning failed: " + registrationResult.getStatus());
-        }
+        ProvisioningDeviceClientRegistrationResult registrationResult = provisioningDeviceClient.registerDeviceSync(additionalData);
 
         ClientOptions options = ClientOptions.builder().modelId(MODEL_ID).build();
 
diff --git a/iothub/device/iot-device-samples/pnp-device-sample/thermostat-device-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/device/Thermostat.java b/iothub/device/iot-device-samples/pnp-device-sample/thermostat-device-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/device/Thermostat.java
index abc99c844c..e754939e85 100644
--- a/iothub/device/iot-device-samples/pnp-device-sample/thermostat-device-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/device/Thermostat.java
+++ b/iothub/device/iot-device-samples/pnp-device-sample/thermostat-device-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/device/Thermostat.java
@@ -69,7 +69,6 @@ public enum StatusCode {
     private static final String registrationId = System.getenv("IOTHUB_DEVICE_DPS_DEVICE_ID");
 
     private static final ProvisioningDeviceClientTransportProtocol provisioningProtocol = ProvisioningDeviceClientTransportProtocol.MQTT;
-    private static final int MAX_TIME_TO_WAIT_FOR_REGISTRATION = 1000; // in milli seconds
 
     // Plug and play features are available over MQTT, MQTT_WS, AMQPS, and AMQPS_WS.
     private static final IotHubClientProtocol protocol = IotHubClientProtocol.MQTT;
@@ -87,33 +86,8 @@ public enum StatusCode {
     private static double maxTemperature = 0.0d;
     private static boolean temperatureReset = true;
 
-    static class ProvisioningStatus
+    public static void main(String[] args) throws Exception
     {
-        ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationInfoClient = new ProvisioningDeviceClientRegistrationResult();
-        Exception exception;
-    }
-
-    static class ProvisioningDeviceClientRegistrationCallbackImpl implements ProvisioningDeviceClientRegistrationCallback
-    {
-        @Override
-        public void run(ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationResult, Exception exception, Object context)
-        {
-            if (context instanceof ProvisioningStatus)
-            {
-                ProvisioningStatus status = (ProvisioningStatus) context;
-                status.provisioningDeviceClientRegistrationInfoClient = provisioningDeviceClientRegistrationResult;
-                status.exception = exception;
-            }
-            else
-            {
-                System.out.println("Received unknown context");
-            }
-        }
-    }
-
-    public static void main(String[] args) throws URISyntaxException, IOException, ProvisioningDeviceClientException, InterruptedException, IotHubClientException, TimeoutException
-    {
-
         // This sample follows the following workflow:
         // -> Initialize device client instance.
         // -> Set handler to receive "targetTemperature" updates, and send the received update over reported property.
@@ -216,7 +190,7 @@ public void run() {
         }).start();
     }
 
-    private static void initializeAndProvisionDevice() throws ProvisioningDeviceClientException, IOException, URISyntaxException, InterruptedException, IotHubClientException
+    private static void initializeAndProvisionDevice() throws Exception
     {
         SecurityProviderSymmetricKey securityClientSymmetricKey = new SecurityProviderSymmetricKey(deviceSymmetricKey.getBytes(StandardCharsets.UTF_8), registrationId);
         ProvisioningDeviceClient provisioningDeviceClient;
@@ -226,34 +200,7 @@ private static void initializeAndProvisionDevice() throws ProvisioningDeviceClie
         AdditionalData additionalData = new AdditionalData();
         additionalData.setProvisioningPayload(String.format("{\"modelId\": \"%s\"}", MODEL_ID));
 
-        final CountDownLatch registrationLatch = new CountDownLatch(1);
-        AtomicReference<ProvisioningDeviceClientRegistrationResult> registrationResultReference = new AtomicReference<>();
-        AtomicReference<Exception> registrationExceptionReference = new AtomicReference<>();
-        provisioningDeviceClient.registerDevice(
-            (provisioningDeviceClientRegistrationResult, e, context) ->
-            {
-                registrationResultReference.set(provisioningDeviceClientRegistrationResult);
-                registrationExceptionReference.set(e);
-                registrationLatch.countDown();
-            },
-            null,
-            additionalData);
-
-        System.out.println("Waiting for Provisioning Service to register");
-        // Time out after 1 minute of waiting. Typically, this operation only takes a few seconds.
-        boolean timedOut = !registrationLatch.await(1, TimeUnit.MINUTES);
-        if (timedOut)
-        {
-            throw new IotHubClientException(IotHubStatusCode.DEVICE_OPERATION_TIMED_OUT, "Timed out waiting for provisioning to finish");
-        }
-
-        ProvisioningDeviceClientRegistrationResult registrationResult = registrationResultReference.get();
-        Exception registrationException = registrationExceptionReference.get();
-
-        if (registrationException != null)
-        {
-            throw new IotHubClientException(IotHubStatusCode.ERROR, "Provisioning failed: " + registrationResult.getStatus());
-        }
+        ProvisioningDeviceClientRegistrationResult registrationResult = provisioningDeviceClient.registerDeviceSync(additionalData);
 
         ClientOptions options = ClientOptions.builder().modelId(MODEL_ID).build();
 
diff --git a/provisioning/provisioning-device-client-samples/provisioning-X509-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningX509Sample.java b/provisioning/provisioning-device-client-samples/provisioning-X509-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningX509Sample.java
index 5e4430d75a..ba50f88163 100644
--- a/provisioning/provisioning-device-client-samples/provisioning-X509-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningX509Sample.java
+++ b/provisioning/provisioning-device-client-samples/provisioning-X509-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningX509Sample.java
@@ -4,9 +4,7 @@
 package samples.com.microsoft.azure.sdk.iot;
 
 import com.microsoft.azure.sdk.iot.device.*;
-import com.microsoft.azure.sdk.iot.device.exceptions.IotHubClientException;
 import com.microsoft.azure.sdk.iot.provisioning.device.*;
-import com.microsoft.azure.sdk.iot.provisioning.device.internal.exceptions.ProvisioningDeviceClientException;
 import com.microsoft.azure.sdk.iot.provisioning.security.SecurityProvider;
 import com.microsoft.azure.sdk.iot.provisioning.security.hsm.SecurityProviderX509Cert;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
@@ -20,12 +18,12 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.StringReader;
+import java.nio.charset.StandardCharsets;
 import java.security.Key;
 import java.security.Security;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
-import java.nio.charset.StandardCharsets;
 import java.util.Collection;
 import java.util.LinkedList;
 import java.util.Scanner;
@@ -36,144 +34,62 @@
 @SuppressWarnings("CommentedOutCode") // Ignored in samples as we use these comments to show other options.
 public class ProvisioningX509Sample
 {
-    private static final String idScope = "[Your ID scope here]";
-    private static final String globalEndpoint = "[Your Provisioning Service Global Endpoint here]";
+    private static final String ID_SCOPE = "[Your ID scope here]";
+    private static final String GLOBAL_ENDPOINT = "[Your Provisioning Service Global Endpoint here]";
     private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.HTTPS;
     //private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.AMQPS;
     //private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.AMQPS_WS;
     //private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.MQTT;
     //private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.MQTT_WS;
-    private static final int MAX_TIME_TO_WAIT_FOR_REGISTRATION = 10000; // in milli seconds
     private static final String leafPublicPem = "<Your Public Leaf Certificate Here>";
     private static final String leafPrivateKeyPem = "<Your Leaf Key Here>";
 
     private static final Collection<String> signerCertificatePemList = new LinkedList<>();
 
-    static class ProvisioningStatus
-    {
-        ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationInfoClient = new ProvisioningDeviceClientRegistrationResult();
-        Exception exception;
-    }
-
-    static class ProvisioningDeviceClientRegistrationCallbackImpl implements ProvisioningDeviceClientRegistrationCallback
-    {
-        @Override
-        public void run(ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationResult, Exception exception, Object context)
-        {
-            if (context instanceof ProvisioningStatus)
-            {
-                ProvisioningStatus status = (ProvisioningStatus) context;
-                status.provisioningDeviceClientRegistrationInfoClient = provisioningDeviceClientRegistrationResult;
-                status.exception = exception;
-            }
-            else
-            {
-                System.out.println("Received unknown context");
-            }
-        }
-    }
-
-    private static class MessageSentCallbackImpl implements MessageSentCallback
-    {
-        @Override
-        public void onMessageSent(Message sentMessage, IotHubClientException exception, Object callbackContext)
-        {
-            System.out.println("Message sent!");
-        }
-    }
-
     public static void main(String[] args) throws Exception
     {
         System.out.println("Starting...");
         System.out.println("Beginning setup.");
-        ProvisioningDeviceClient provisioningDeviceClient = null;
-        DeviceClient deviceClient = null;
-        try
-        {
-            ProvisioningStatus provisioningStatus = new ProvisioningStatus();
-
-            // For group enrollment uncomment this line
-            //signerCertificatePemList.add("<Your Signer/intermediate Certificate Here>");
-
-            X509Certificate leafPublicCert = parsePublicKeyCertificate(leafPublicPem);
-            Key leafPrivateKey = parsePrivateKey(leafPrivateKeyPem);
-            Collection<X509Certificate> signerCertificates = new LinkedList<>();
-            for (String signerCertificatePem : signerCertificatePemList)
-            {
-                signerCertificates.add(parsePublicKeyCertificate(signerCertificatePem));
-            }
-
-            SecurityProvider securityProviderX509 = new SecurityProviderX509Cert(leafPublicCert, leafPrivateKey, signerCertificates);
-            provisioningDeviceClient = ProvisioningDeviceClient.create(globalEndpoint, idScope, PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL,
-                                                                       securityProviderX509);
-
-            provisioningDeviceClient.registerDevice(new ProvisioningDeviceClientRegistrationCallbackImpl(), provisioningStatus);
-
-            while (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() != ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
-            {
-                if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ERROR ||
-                        provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_DISABLED ||
-                        provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_FAILED )
-
-                {
-                    provisioningStatus.exception.printStackTrace();
-                    System.out.println("Registration error, bailing out");
-                    break;
-                }
-                System.out.println("Waiting for Provisioning Service to register");
-                Thread.sleep(MAX_TIME_TO_WAIT_FOR_REGISTRATION);
-            }
-
-            if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
-            {
-                System.out.println("IotHUb Uri : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri());
-                System.out.println("Device ID : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId());
-
-                // connect to iothub
-                String iotHubUri = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri();
-                String deviceId = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId();
-                try
-                {
-                    deviceClient = new DeviceClient(iotHubUri, deviceId, securityProviderX509, IotHubClientProtocol.MQTT);
-                    deviceClient.open(false);
-                    Message messageToSendFromDeviceToHub =  new Message("Whatever message you would like to send");
-
-                    System.out.println("Sending message from device to IoT Hub...");
-                    deviceClient.sendEventAsync(messageToSendFromDeviceToHub, new MessageSentCallbackImpl(), null);
-                }
-                catch (IOException e)
-                {
-                    System.out.println("Device client threw an exception: " + e.getMessage());
-                    if (deviceClient != null)
-                    {
-                        deviceClient.close();
-                    }
-                }
-            }
-        }
-        catch (ProvisioningDeviceClientException | InterruptedException e)
+
+        // For group enrollment uncomment this line
+        //signerCertificatePemList.add("<Your Signer/intermediate Certificate Here>");
+
+        X509Certificate leafPublicCert = parsePublicKeyCertificate(leafPublicPem);
+        Key leafPrivateKey = parsePrivateKey(leafPrivateKeyPem);
+        Collection<X509Certificate> signerCertificates = new LinkedList<>();
+        for (String signerCertificatePem : signerCertificatePemList)
         {
-            System.out.println("Provisioning Device Client threw an exception" + e.getMessage());
-            if (provisioningDeviceClient != null)
-            {
-                provisioningDeviceClient.close();
-            }
+            signerCertificates.add(parsePublicKeyCertificate(signerCertificatePem));
         }
 
-        System.out.println("Press any key to exit...");
+        SecurityProvider securityProviderX509 = new SecurityProviderX509Cert(leafPublicCert, leafPrivateKey, signerCertificates);
+        ProvisioningDeviceClient provisioningDeviceClient = ProvisioningDeviceClient.create(
+            GLOBAL_ENDPOINT,
+            ID_SCOPE,
+            PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL,
+            securityProviderX509);
 
-        Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8.name());
-        scanner.nextLine();
+        ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationResult = provisioningDeviceClient.registerDeviceSync();
+        provisioningDeviceClient.close();
 
-        System.out.println("Shutting down...");
-        if (provisioningDeviceClient != null)
-        {
-            provisioningDeviceClient.close();
-        }
-        if (deviceClient != null)
+        if (provisioningDeviceClientRegistrationResult.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
         {
+            System.out.println("IotHub Uri : " + provisioningDeviceClientRegistrationResult.getIothubUri());
+            System.out.println("Device ID : " + provisioningDeviceClientRegistrationResult.getDeviceId());
+
+            // connect to iothub
+            String iotHubUri = provisioningDeviceClientRegistrationResult.getIothubUri();
+            String deviceId = provisioningDeviceClientRegistrationResult.getDeviceId();
+            DeviceClient deviceClient = new DeviceClient(iotHubUri, deviceId, securityProviderX509, IotHubClientProtocol.MQTT);
+            deviceClient.open(false);
+
+            System.out.println("Sending message from device to IoT Hub...");
+            deviceClient.sendEvent(new Message("Hello world!"));
             deviceClient.close();
         }
+
+        System.out.println("Press any key to exit...");
+        new Scanner(System.in, StandardCharsets.UTF_8.name()).nextLine();
     }
 
     private static Key parsePrivateKey(String privateKeyString) throws IOException
diff --git a/provisioning/provisioning-device-client-samples/provisioning-symmetrickey-group-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningSymmetricKeyEnrollmentGroupSample.java b/provisioning/provisioning-device-client-samples/provisioning-symmetrickey-group-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningSymmetricKeyEnrollmentGroupSample.java
index 26d8367313..557ad51864 100644
--- a/provisioning/provisioning-device-client-samples/provisioning-symmetrickey-group-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningSymmetricKeyEnrollmentGroupSample.java
+++ b/provisioning/provisioning-device-client-samples/provisioning-symmetrickey-group-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningSymmetricKeyEnrollmentGroupSample.java
@@ -8,12 +8,9 @@
 package samples.com.microsoft.azure.sdk.iot;
 
 import com.microsoft.azure.sdk.iot.device.*;
-import com.microsoft.azure.sdk.iot.device.exceptions.IotHubClientException;
 import com.microsoft.azure.sdk.iot.provisioning.device.*;
-import com.microsoft.azure.sdk.iot.provisioning.device.internal.exceptions.ProvisioningDeviceClientException;
 import com.microsoft.azure.sdk.iot.provisioning.security.SecurityProviderSymmetricKey;
 
-import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Scanner;
 
@@ -27,10 +24,11 @@
 public class ProvisioningSymmetricKeyEnrollmentGroupSample
 {
     // The scope Id of your DPS instance. This value can be retrieved from the Azure Portal
-    private static final String SCOPE_ID = "[Your scope ID here]";
+    private static final String ID_SCOPE = "[Your ID scope here]";
 
-    // Typically "global.azure-devices-provisioning.net"
-    private static final String GLOBAL_ENDPOINT = "[Your Provisioning Service Global Endpoint here]";
+    // Note that a different value is required here when connecting to a private or government cloud instance. This
+    // value is fine for most DPS instances otherwise.
+    private static final String GLOBAL_ENDPOINT = "global.azure-devices-provisioning.net";
 
     // Not to be confused with the symmetric key of the enrollment group itself, this key is derived from the symmetric
     // key of the enrollment group and the desired device id of the device to provision. See the
@@ -49,127 +47,44 @@ public class ProvisioningSymmetricKeyEnrollmentGroupSample
     //private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.AMQPS;
     //private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.AMQPS_WS;
 
-    private static final int MAX_TIME_TO_WAIT_FOR_REGISTRATION = 10000; // in milliseconds
-
-    static class ProvisioningStatus
-    {
-        ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationInfoClient = new ProvisioningDeviceClientRegistrationResult();
-        Exception exception;
-    }
-
-    static class ProvisioningDeviceClientRegistrationCallbackImpl implements ProvisioningDeviceClientRegistrationCallback
-    {
-        @Override
-        public void run(ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationResult, Exception exception, Object context)
-        {
-            if (context instanceof ProvisioningStatus)
-            {
-                ProvisioningStatus status = (ProvisioningStatus) context;
-                status.provisioningDeviceClientRegistrationInfoClient = provisioningDeviceClientRegistrationResult;
-                status.exception = exception;
-            }
-            else
-            {
-                System.out.println("Received unknown context");
-            }
-        }
-    }
-
-    private static class MessageSentCallbackImpl implements MessageSentCallback
-    {
-        @Override
-        public void onMessageSent(Message sentMessage, IotHubClientException exception, Object callbackContext)
-        {
-            IotHubStatusCode status = exception == null ? IotHubStatusCode.OK : exception.getStatusCode();
-            System.out.println("Message received! Response status: " + status);
-        }
-    }
-
     public static void main(String[] args) throws Exception
     {
         System.out.println("Starting...");
         System.out.println("Beginning setup.");
-        SecurityProviderSymmetricKey securityClientSymmetricKey;
-        Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8.name());
-        DeviceClient deviceClient = null;
 
         // For the sake of security, you shouldn't save keys into String variables as that places them in heap memory. For the sake
         // of simplicity within this sample, though, we will save it as a string. Typically this key would be loaded as byte[] so that
         // it can be removed from stack memory.
         byte[] derivedSymmetricKey = DERIVED_ENROLLMENT_GROUP_SYMMETRIC_KEY.getBytes(StandardCharsets.UTF_8);
 
-        securityClientSymmetricKey = new SecurityProviderSymmetricKey(derivedSymmetricKey, PROVISIONED_DEVICE_ID);
+        SecurityProviderSymmetricKey securityClientSymmetricKey = new SecurityProviderSymmetricKey(derivedSymmetricKey, PROVISIONED_DEVICE_ID);
 
-        ProvisioningDeviceClient provisioningDeviceClient = null;
-        try
-        {
-            ProvisioningStatus provisioningStatus = new ProvisioningStatus();
-
-            provisioningDeviceClient = ProvisioningDeviceClient.create(GLOBAL_ENDPOINT, SCOPE_ID, PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL, securityClientSymmetricKey);
-
-            provisioningDeviceClient.registerDevice(new ProvisioningDeviceClientRegistrationCallbackImpl(), provisioningStatus);
-            while (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() != ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
-            {
-                if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ERROR ||
-                        provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_DISABLED ||
-                        provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_FAILED)
-                {
-                    provisioningStatus.exception.printStackTrace();
-                    System.out.println("Registration error, bailing out");
-                    break;
-                }
-                System.out.println("Waiting for Provisioning Service to register");
-                Thread.sleep(MAX_TIME_TO_WAIT_FOR_REGISTRATION);
-            }
-
-            if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
-            {
-                System.out.println("IotHUb Uri : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri());
-                System.out.println("Device ID : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId());
-
-                // connect to iothub
-                String iotHubUri = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri();
-                String deviceId = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId();
-                try
-                {
-                    deviceClient = new DeviceClient(iotHubUri, deviceId, securityClientSymmetricKey, IotHubClientProtocol.MQTT);
-                    deviceClient.open(false);
-                    Message messageToSendFromDeviceToHub =  new Message("Whatever message you would like to send");
-
-                    System.out.println("Sending message from device to IoT Hub...");
-                    deviceClient.sendEventAsync(messageToSendFromDeviceToHub, new MessageSentCallbackImpl(), null);
-                }
-                catch (IOException e)
-                {
-                    System.out.println("Device client threw an exception: " + e.getMessage());
-                    if (deviceClient != null)
-                    {
-                        deviceClient.close();
-                    }
-                }
-            }
-        }
-        catch (ProvisioningDeviceClientException | InterruptedException e)
-        {
-            System.out.println("Provisioning Device Client threw an exception" + e.getMessage());
-            if (provisioningDeviceClient != null)
-            {
-                provisioningDeviceClient.close();
-            }
-        }
+        ProvisioningDeviceClient provisioningDeviceClient = ProvisioningDeviceClient.create(
+            GLOBAL_ENDPOINT,
+            ID_SCOPE,
+            PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL,
+            securityClientSymmetricKey);
 
-        System.out.println("Press any key to exit...");
+        ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationResult = provisioningDeviceClient.registerDeviceSync();
+        provisioningDeviceClient.close();
 
-        scanner.nextLine();
-        if (provisioningDeviceClient != null)
-        {
-            provisioningDeviceClient.close();
-        }
-        if (deviceClient != null)
+        if (provisioningDeviceClientRegistrationResult.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
         {
+            System.out.println("IotHub Uri : " + provisioningDeviceClientRegistrationResult.getIothubUri());
+            System.out.println("Device ID : " + provisioningDeviceClientRegistrationResult.getDeviceId());
+
+            // connect to iothub
+            String iotHubUri = provisioningDeviceClientRegistrationResult.getIothubUri();
+            String deviceId = provisioningDeviceClientRegistrationResult.getDeviceId();
+            DeviceClient deviceClient = new DeviceClient(iotHubUri, deviceId, securityClientSymmetricKey, IotHubClientProtocol.MQTT);
+            deviceClient.open(false);
+
+            System.out.println("Sending message from device to IoT Hub...");
+            deviceClient.sendEvent(new Message("Hello world!"));
             deviceClient.close();
         }
 
-        System.out.println("Shutting down...");
+        System.out.println("Press any key to exit...");
+        new Scanner(System.in, StandardCharsets.UTF_8.name()).nextLine();
     }
 }
diff --git a/provisioning/provisioning-device-client-samples/provisioning-symmetrickey-individual-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningSymmetricKeyIndividualEnrollmentSample.java b/provisioning/provisioning-device-client-samples/provisioning-symmetrickey-individual-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningSymmetricKeyIndividualEnrollmentSample.java
index 1e31c9f5f8..226d26a814 100644
--- a/provisioning/provisioning-device-client-samples/provisioning-symmetrickey-individual-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningSymmetricKeyIndividualEnrollmentSample.java
+++ b/provisioning/provisioning-device-client-samples/provisioning-symmetrickey-individual-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningSymmetricKeyIndividualEnrollmentSample.java
@@ -24,7 +24,7 @@
 public class ProvisioningSymmetricKeyIndividualEnrollmentSample
 {
     // The scope Id of your DPS instance. This value can be retrieved from the Azure Portal
-    private static final String SCOPE_ID = "[Your scope ID here]";
+    private static final String ID_SCOPE = "[Your ID scope here]";
 
     // Typically "global.azure-devices-provisioning.net"
     private static final String GLOBAL_ENDPOINT = "[Your Provisioning Service Global Endpoint here]";
@@ -46,122 +46,40 @@ public class ProvisioningSymmetricKeyIndividualEnrollmentSample
     //private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.AMQPS;
     //private static final ProvisioningDeviceClientTransportProtocol PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL = ProvisioningDeviceClientTransportProtocol.AMQPS_WS;
 
-    private static final int MAX_TIME_TO_WAIT_FOR_REGISTRATION = 10000; // in milliseconds
-
-    static class ProvisioningStatus
-    {
-        ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationInfoClient = new ProvisioningDeviceClientRegistrationResult();
-        Exception exception;
-    }
-
-    static class ProvisioningDeviceClientRegistrationCallbackImpl implements ProvisioningDeviceClientRegistrationCallback
-    {
-        @Override
-        public void run(ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationResult, Exception exception, Object context)
-        {
-            if (context instanceof ProvisioningStatus)
-            {
-                ProvisioningStatus status = (ProvisioningStatus) context;
-                status.provisioningDeviceClientRegistrationInfoClient = provisioningDeviceClientRegistrationResult;
-                status.exception = exception;
-            }
-            else
-            {
-                System.out.println("Received unknown context");
-            }
-        }
-    }
-
-    private static class MessageSentCallbackImpl implements MessageSentCallback
-    {
-        @Override
-        public void onMessageSent(Message sentMessage, IotHubClientException exception, Object callbackContext)
-        {
-            IotHubStatusCode status = exception == null ? IotHubStatusCode.OK : exception.getStatusCode();
-            System.out.println("Message received! Response status: " + status);
-        }
-    }
-
     public static void main(String[] args) throws Exception
     {
         System.out.println("Starting...");
         System.out.println("Beginning setup.");
         SecurityProviderSymmetricKey securityClientSymmetricKey;
-        Scanner scanner = new Scanner(System.in, StandardCharsets.UTF_8.name());
-        DeviceClient deviceClient = null;
 
         securityClientSymmetricKey = new SecurityProviderSymmetricKey(SYMMETRIC_KEY.getBytes(StandardCharsets.UTF_8), REGISTRATION_ID);
 
-        ProvisioningDeviceClient provisioningDeviceClient = null;
-        try
-        {
-            ProvisioningStatus provisioningStatus = new ProvisioningStatus();
-
-            provisioningDeviceClient = ProvisioningDeviceClient.create(GLOBAL_ENDPOINT, SCOPE_ID, PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL, securityClientSymmetricKey);
-
-            provisioningDeviceClient.registerDevice(new ProvisioningDeviceClientRegistrationCallbackImpl(), provisioningStatus);
-            while (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() != ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
-            {
-                if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ERROR ||
-                        provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_DISABLED ||
-                        provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_FAILED)
-                {
-                    provisioningStatus.exception.printStackTrace();
-                    System.out.println("Registration error, bailing out");
-                    break;
-                }
-                System.out.println("Waiting for Provisioning Service to register");
-                Thread.sleep(MAX_TIME_TO_WAIT_FOR_REGISTRATION);
-            }
-
-            if (provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
-            {
-                System.out.println("IotHUb Uri : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri());
-                System.out.println("Device ID : " + provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId());
-
-                // connect to iothub
-                String iotHubUri = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getIothubUri();
-                String deviceId = provisioningStatus.provisioningDeviceClientRegistrationInfoClient.getDeviceId();
-                try
-                {
-                    deviceClient = new DeviceClient(iotHubUri, deviceId, securityClientSymmetricKey, IotHubClientProtocol.MQTT);
-                    deviceClient.open(false);
-                    Message messageToSendFromDeviceToHub =  new Message("Whatever message you would like to send");
-
-                    System.out.println("Sending message from device to IoT Hub...");
-                    deviceClient.sendEventAsync(messageToSendFromDeviceToHub, new MessageSentCallbackImpl(), null);
-                }
-                catch (IOException e)
-                {
-                    System.out.println("Device client threw an exception: " + e.getMessage());
-                    if (deviceClient != null)
-                    {
-                        deviceClient.close();
-                    }
-                }
-            }
-        }
-        catch (ProvisioningDeviceClientException | InterruptedException e)
-        {
-            System.out.println("Provisioning Device Client threw an exception" + e.getMessage());
-            if (provisioningDeviceClient != null)
-            {
-                provisioningDeviceClient.close();
-            }
-        }
+        ProvisioningDeviceClient provisioningDeviceClient = ProvisioningDeviceClient.create(
+            GLOBAL_ENDPOINT,
+            ID_SCOPE,
+            PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL,
+            securityClientSymmetricKey);
 
-        System.out.println("Press any key to exit...");
+        ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationResult = provisioningDeviceClient.registerDeviceSync();
+        provisioningDeviceClient.close();
 
-        scanner.nextLine();
-        if (provisioningDeviceClient != null)
-        {
-            provisioningDeviceClient.close();
-        }
-        if (deviceClient != null)
+        if (provisioningDeviceClientRegistrationResult.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
         {
+            System.out.println("IotHub Uri : " + provisioningDeviceClientRegistrationResult.getIothubUri());
+            System.out.println("Device ID : " + provisioningDeviceClientRegistrationResult.getDeviceId());
+
+            // connect to iothub
+            String iotHubUri = provisioningDeviceClientRegistrationResult.getIothubUri();
+            String deviceId = provisioningDeviceClientRegistrationResult.getDeviceId();
+            DeviceClient deviceClient = new DeviceClient(iotHubUri, deviceId, securityClientSymmetricKey, IotHubClientProtocol.MQTT);
+            deviceClient.open(false);
+
+            System.out.println("Sending message from device to IoT Hub...");
+            deviceClient.sendEvent(new Message("Hello world!"));
             deviceClient.close();
         }
 
-        System.out.println("Shutting down...");
+        System.out.println("Press any key to exit...");
+        new Scanner(System.in, StandardCharsets.UTF_8.name()).nextLine();
     }
 }
diff --git a/provisioning/provisioning-device-client-samples/provisioning-tpm-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningTpmSample.java b/provisioning/provisioning-device-client-samples/provisioning-tpm-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningTpmSample.java
index 17ad3942eb..c4cab467d6 100644
--- a/provisioning/provisioning-device-client-samples/provisioning-tpm-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningTpmSample.java
+++ b/provisioning/provisioning-device-client-samples/provisioning-tpm-sample/src/main/java/samples/com/microsoft/azure/sdk/iot/ProvisioningTpmSample.java
@@ -9,15 +9,12 @@
 
 import com.microsoft.azure.sdk.iot.device.*;
 import com.microsoft.azure.sdk.iot.provisioning.device.*;
-import com.microsoft.azure.sdk.iot.provisioning.device.internal.exceptions.ProvisioningDeviceClientException;
 import com.microsoft.azure.sdk.iot.provisioning.security.SecurityProviderTpm;
 import com.microsoft.azure.sdk.iot.provisioning.security.exceptions.SecurityProviderException;
 import com.microsoft.azure.sdk.iot.provisioning.security.hsm.SecurityProviderTPMEmulator;
 
-import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import java.util.Scanner;
-import java.util.concurrent.atomic.AtomicReference;
 
 import static org.apache.commons.codec.binary.Base64.encodeBase64;
 
@@ -27,7 +24,7 @@
 @SuppressWarnings("CommentedOutCode") // Ignored in samples as we use these comments to show other options.
 public class ProvisioningTpmSample
 {
-    private static final String SCOPE_ID = "[Your scope ID here]";
+    private static final String ID_SCOPE = "[Your ID scope here]";
 
     // Note that a different value is required here when connecting to a private or government cloud instance. This
     // value is fine for most DPS instances otherwise.
@@ -64,100 +61,32 @@ public static void main(String[] args) throws Exception
             System.exit(-1);
         }
 
-        ProvisioningDeviceClient provisioningDeviceClient = null;
-        try
-        {
+        ProvisioningDeviceClient provisioningDeviceClient = ProvisioningDeviceClient.create(
+            GLOBAL_ENDPOINT,
+            ID_SCOPE,
+            PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL,
+            securityClientTPMEmulator);
 
-            provisioningDeviceClient = ProvisioningDeviceClient.create(GLOBAL_ENDPOINT, SCOPE_ID, PROVISIONING_DEVICE_CLIENT_TRANSPORT_PROTOCOL, securityClientTPMEmulator);
-
-            final Object deviceRegistrationLock = new Object();
-            AtomicReference<ProvisioningDeviceClientRegistrationResult> registrationResultReference = new AtomicReference<>();
-            AtomicReference<Exception> registrationExceptionReference = new AtomicReference<>();
-
-            provisioningDeviceClient.registerDevice(
-                (callbackRegistrationResult, callbackException, callbackContext) -> {
-                    // This callback function executes once the registration request has completed
-                    // (successfully or unsuccessfully) with the details of the new registration
-                    // including what IoT hub it was provisioned to, what its device Id is, and more.
-
-                    // Save the returned registration result and exception (if there was one)
-                    registrationResultReference.set(callbackRegistrationResult);
-                    registrationExceptionReference.set(callbackException);
-
-                    synchronized (deviceRegistrationLock)
-                    {
-                        // Unlock the deviceRegistrationLock so the sample can continue
-                        deviceRegistrationLock.notify();
-                    }
-                },
-                null);
-
-            System.out.println("Waiting for the Provisioning service to finish your device registration.");
-            synchronized (deviceRegistrationLock)
-            {
-                deviceRegistrationLock.wait(MAX_TIME_TO_WAIT_FOR_REGISTRATION);
-            }
-
-            ProvisioningDeviceClientRegistrationResult registrationResult = registrationResultReference.get();
-            Exception registrationException = registrationExceptionReference.get();
-
-            if (registrationException != null)
-            {
-                System.out.println("Encountered an exception while registering your device");
-                registrationException.printStackTrace();
-                System.exit(-1);
-            }
-
-            if (registrationResult.getProvisioningDeviceClientStatus() != ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
-            {
-                System.out.println("Device provisioning completed unsuccessfully. Encountered an unexpected registration status: " + registrationResult.getStatus());
-                System.exit(-1);
-            }
-            else
-            {
-                System.out.println("Device provisioning completed successfully");
-                System.out.println("IotHUb Uri : " + registrationResult.getIothubUri());
-                System.out.println("Device ID : " + registrationResult.getDeviceId());
-
-                // connect to iothub
-                String iotHubUri = registrationResult.getIothubUri();
-                String deviceId = registrationResult.getDeviceId();
-                DeviceClient deviceClient = null;
-                try
-                {
-                    deviceClient = new DeviceClient(iotHubUri, deviceId, securityClientTPMEmulator, IotHubClientProtocol.MQTT);
-                    deviceClient.open(false);
-                    Message messageToSendFromDeviceToHub =  new Message("Whatever message you would like to send");
-
-                    System.out.println("Sending message from device to IoT Hub...");
-                    deviceClient.sendEvent(messageToSendFromDeviceToHub);
-                }
-                catch (IOException e)
-                {
-                    System.out.println("Device client threw an exception: " + e.getMessage());
-                    if (deviceClient != null)
-                    {
-                        deviceClient.close();
-                    }
-                }
-            }
-        }
-        catch (ProvisioningDeviceClientException | InterruptedException e)
+        ProvisioningDeviceClientRegistrationResult provisioningDeviceClientRegistrationResult = provisioningDeviceClient.registerDeviceSync();
+        provisioningDeviceClient.close();
+
+        if (provisioningDeviceClientRegistrationResult.getProvisioningDeviceClientStatus() == ProvisioningDeviceClientStatus.PROVISIONING_DEVICE_STATUS_ASSIGNED)
         {
-            System.out.println("Provisioning Device Client threw an exception" + e.getMessage());
-            if (provisioningDeviceClient != null)
-            {
-                provisioningDeviceClient.close();
-            }
+            System.out.println("IotHub Uri : " + provisioningDeviceClientRegistrationResult.getIothubUri());
+            System.out.println("Device ID : " + provisioningDeviceClientRegistrationResult.getDeviceId());
+
+            // connect to iothub
+            String iotHubUri = provisioningDeviceClientRegistrationResult.getIothubUri();
+            String deviceId = provisioningDeviceClientRegistrationResult.getDeviceId();
+            DeviceClient deviceClient = new DeviceClient(iotHubUri, deviceId, securityClientTPMEmulator, IotHubClientProtocol.MQTT);
+            deviceClient.open(false);
+
+            System.out.println("Sending message from device to IoT Hub...");
+            deviceClient.sendEvent(new Message("Hello world!"));
+            deviceClient.close();
         }
 
         System.out.println("Press any key to exit...");
         scanner.nextLine();
-
-        System.out.println("Shutting down...");
-        if (provisioningDeviceClient != null)
-        {
-            provisioningDeviceClient.close();
-        }
     }
 }
diff --git a/provisioning/provisioning-device-client/src/main/java/com/microsoft/azure/sdk/iot/provisioning/device/ProvisioningDeviceClient.java b/provisioning/provisioning-device-client/src/main/java/com/microsoft/azure/sdk/iot/provisioning/device/ProvisioningDeviceClient.java
index 7a70a3a2df..eef6158911 100644
--- a/provisioning/provisioning-device-client/src/main/java/com/microsoft/azure/sdk/iot/provisioning/device/ProvisioningDeviceClient.java
+++ b/provisioning/provisioning-device-client/src/main/java/com/microsoft/azure/sdk/iot/provisioning/device/ProvisioningDeviceClient.java
@@ -9,21 +9,21 @@
 
 import com.microsoft.azure.sdk.iot.provisioning.device.internal.ProvisioningDeviceClientConfig;
 import com.microsoft.azure.sdk.iot.provisioning.device.internal.SDKUtils;
+import com.microsoft.azure.sdk.iot.provisioning.device.internal.exceptions.ProvisioningDeviceHubException;
 import com.microsoft.azure.sdk.iot.provisioning.device.internal.task.ProvisioningTask;
 import com.microsoft.azure.sdk.iot.provisioning.device.internal.contract.ProvisioningDeviceClientContract;
 import com.microsoft.azure.sdk.iot.provisioning.device.internal.exceptions.ProvisioningDeviceClientException;
 import com.microsoft.azure.sdk.iot.provisioning.security.SecurityProvider;
-import com.microsoft.azure.sdk.iot.provisioning.device.AdditionalData;
 import lombok.extern.slf4j.Slf4j;
 
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.AtomicReference;
 
 @Slf4j
 public class ProvisioningDeviceClient
 {
     private static final int MAX_THREADS_TO_RUN = 1;
+    private static final int DEFAULT_TIMEOUT_SECONDS = 60;
 
     private final ProvisioningDeviceClientConfig provisioningDeviceClientConfig;
     private final ProvisioningDeviceClientContract provisioningDeviceClientContract;
@@ -47,29 +47,24 @@ private ProvisioningDeviceClient(String globalEndpoint, String idScope, Provisio
     {
         if (globalEndpoint == null || globalEndpoint.isEmpty())
         {
-            //SRS_ProvisioningDeviceClient_25_001: [ The constructor shall throw IllegalArgumentException if globalEndpoint is null or empty. ]
             throw new IllegalArgumentException("global endpoint cannot be null or empty");
         }
 
         if (idScope == null || idScope.isEmpty())
         {
-            //SRS_ProvisioningDeviceClient_25_002: [ The constructor shall throw IllegalArgumentException if idScope is null or empty. ]
             throw new IllegalArgumentException("scope id cannot be null or empty");
         }
 
         if (protocol == null)
         {
-            //SRS_ProvisioningDeviceClient_25_003: [ The constructor shall throw IllegalArgumentException if protocol is null. ]
             throw new IllegalArgumentException("protocol cannot be null");
         }
 
         if (securityProvider == null)
         {
-            //SRS_ProvisioningDeviceClient_25_004: [ The constructor shall throw IllegalArgumentException if securityProvider is null. ]
             throw new IllegalArgumentException("Security provider cannot be null");
         }
 
-        //SRS_ProvisioningDeviceClient_25_005: [ The constructor shall create provisioningDeviceClientConfig and set all the provided values to it.. ]
         this.provisioningDeviceClientConfig = new ProvisioningDeviceClientConfig();
 
         this.provisioningDeviceClientConfig.setProvisioningServiceGlobalEndpoint(globalEndpoint);
@@ -77,16 +72,14 @@ private ProvisioningDeviceClient(String globalEndpoint, String idScope, Provisio
         this.provisioningDeviceClientConfig.setProtocol(protocol);
         this.provisioningDeviceClientConfig.setSecurityProvider(securityProvider);
 
-        //SRS_ProvisioningDeviceClient_25_006: [ The constructor shall create provisioningDeviceClientContract with the given config. ]
         this.provisioningDeviceClientContract = ProvisioningDeviceClientContract.createProvisioningContract(this.provisioningDeviceClientConfig);
-        //SRS_ProvisioningDeviceClient_25_007: [ The constructor shall create an executor service with fixed thread pool of size 1. ]
         this.executor = Executors.newFixedThreadPool(MAX_THREADS_TO_RUN);
 
         log.debug("Initialized a ProvisioningDeviceClient instance using SDK version {}", SDKUtils.PROVISIONING_DEVICE_CLIENT_VERSION);
     }
 
     /**
-     * Register's a device with the service and provides you with iothub uri and the registered device.
+     * Asynchronously registers a device with the service and provides you with iothub uri and the registered device.
      * @param provisioningDeviceClientRegistrationCallback Callback where you can retrieve the status of registration like iothub uri and the registered device or
      *                                                     any exception that was caused during registration process. Cannot be {@code null}.
      * @param context Context for the callback. Can be {@code null}.
@@ -96,21 +89,18 @@ public void registerDevice(ProvisioningDeviceClientRegistrationCallback provisio
     {
         if (provisioningDeviceClientRegistrationCallback == null)
         {
-            //SRS_ProvisioningDeviceClient_25_008: [ This method shall throw IllegalArgumentException if provisioningDeviceClientRegistrationCallback is null. ]
             throw new IllegalArgumentException("registration callback cannot be null");
         }
 
-        //SRS_ProvisioningDeviceClient_25_009: [ This method shall set the config with the callback. ]
         this.provisioningDeviceClientConfig.setRegistrationCallback(provisioningDeviceClientRegistrationCallback, context);
 
-        //SRS_ProvisioningDeviceClient_25_010: [ This method shall start the executor with the ProvisioningTask. ]
         log.debug("Starting provisioning thread...");
         Callable<Object> provisioningTask = new ProvisioningTask(this.provisioningDeviceClientConfig, this.provisioningDeviceClientContract);
         executor.submit(provisioningTask);
     }
 
     /**
-     * Register's a device with the service and provides you with iothub uri and the registered device.
+     * Asynchronously registers a device with the service and provides you with iothub uri and the registered device.
      * @param provisioningDeviceClientRegistrationCallback Callback where you can retrieve the status of registration like iothub uri and the registered device or
      *                                                     any exception that was caused during registration process. Cannot be {@code null}.
      * @param context Context for the callback. Can be {@code null}.
@@ -121,27 +111,108 @@ public void registerDevice(ProvisioningDeviceClientRegistrationCallback provisio
     {
         if (provisioningDeviceClientRegistrationCallback == null)
         {
-            //SRS_ProvisioningDeviceClient_25_008: [ This method shall throw IllegalArgumentException if provisioningDeviceClientRegistrationCallback is null. ]
             throw new IllegalArgumentException("registration callback cannot be null");
         }
 
         this.provisioningDeviceClientConfig.setPayload(additionalData.getProvisioningPayload());
 
-        //SRS_ProvisioningDeviceClient_25_009: [ This method shall set the config with the callback. ]
         this.provisioningDeviceClientConfig.setRegistrationCallback(provisioningDeviceClientRegistrationCallback, context);
 
-        //SRS_ProvisioningDeviceClient_25_010: [ This method shall start the executor with the ProvisioningTask. ]
         log.debug("Starting provisioning thread...");
         ProvisioningTask provisioningTask = new ProvisioningTask(this.provisioningDeviceClientConfig, this.provisioningDeviceClientContract);
         executor.submit(provisioningTask);
     }
 
+    /**
+     * Synchronously registers a device with the service and provides you with iothub uri and the registered device.
+     * @return the details of the finished provisioning.
+     * @throws ProvisioningDeviceClientException If any exception is thrown during provisioning.
+     * @throws InterruptedException If this operation is interrupted while waiting for the provisioning to finish.
+     */
+    public ProvisioningDeviceClientRegistrationResult registerDeviceSync() throws ProvisioningDeviceClientException, InterruptedException
+    {
+        return registerDeviceSync(DEFAULT_TIMEOUT_SECONDS);
+    }
+
+    /**
+     * Synchronously registers a device with the service and provides you with iothub uri and the registered device.
+     * @param additionalData Additional data for device registration.
+     * @return the details of the finished provisioning.
+     * @throws ProvisioningDeviceClientException If any exception is thrown during provisioning.
+     * @throws InterruptedException If this operation is interrupted while waiting for the provisioning to finish.
+     */
+    public ProvisioningDeviceClientRegistrationResult registerDeviceSync(AdditionalData additionalData) throws ProvisioningDeviceClientException, InterruptedException
+    {
+        return registerDeviceSync(additionalData, DEFAULT_TIMEOUT_SECONDS);
+    }
+
+    /**
+     * Synchronously registers a device with the service and provides you with iothub uri and the registered device.
+     * @param timeout the amount of time, in seconds, to wait for provisioning to finish. If this operation exceeds the
+     * provided timeout, it will throw {@link ProvisioningDeviceClientException}.
+     * @return the details of the finished provisioning.
+     * @throws ProvisioningDeviceClientException If any exception is thrown during provisioning.
+     * @throws InterruptedException If this operation is interrupted while waiting for the provisioning to finish.
+     */
+    public ProvisioningDeviceClientRegistrationResult registerDeviceSync(int timeout) throws ProvisioningDeviceClientException, InterruptedException
+    {
+        return registerDeviceSync(null, timeout);
+    }
+
+    /**
+     * Synchronously registers a device with the service and provides you with iothub uri and the registered device.
+     * @param additionalData Additional data for device registration.
+     * @param timeout the amount of time, in seconds, to wait for provisioning to finish. If this operation exceeds the
+     * provided timeout, it will throw {@link ProvisioningDeviceClientException}.
+     * @return the details of the finished provisioning.
+     * @throws ProvisioningDeviceClientException If any exception is thrown during provisioning.
+     * @throws InterruptedException If this operation is interrupted while waiting for the provisioning to finish.
+     */
+    public ProvisioningDeviceClientRegistrationResult registerDeviceSync(AdditionalData additionalData, int timeout) throws ProvisioningDeviceClientException, InterruptedException
+    {
+        final CountDownLatch registrationLatch = new CountDownLatch(1);
+        AtomicReference<ProvisioningDeviceClientRegistrationResult> registrationResultReference = new AtomicReference<>();
+        AtomicReference<Exception> registrationExceptionReference = new AtomicReference<>();
+        registerDevice(
+            (provisioningDeviceClientRegistrationResult, e, context) ->
+            {
+                registrationResultReference.set(provisioningDeviceClientRegistrationResult);
+                registrationExceptionReference.set(e);
+                registrationLatch.countDown();
+            },
+            null);
+
+        // Wait until registration finishes or for a max amount of time
+        boolean timedOut = !registrationLatch.await(timeout, TimeUnit.SECONDS);
+        if (timedOut)
+        {
+            throw new ProvisioningDeviceClientException("Timed out waiting for registration to complete");
+        }
+
+        ProvisioningDeviceClientRegistrationResult registrationResult = registrationResultReference.get();
+        Exception registrationException = registrationExceptionReference.get();
+
+        if (registrationException != null)
+        {
+            if (registrationException instanceof ProvisioningDeviceHubException)
+            {
+                throw (ProvisioningDeviceHubException) registrationException;
+            }
+            else
+            {
+                // This shouldn't happen because the registration exception should always be of type ProvisioningDeviceHubException
+                throw new ProvisioningDeviceClientException("Failed to register", registrationException);
+            }
+        }
+
+        return registrationResult;
+    }
+
     /**
      * Closes all the executors opened by the client if they have not already closed.
      */
     public void close()
     {
-        //SRS_ProvisioningDeviceClient_25_011: [ This method shall check if executor is terminated and if not shall shutdown the executor. ]
         if (executor != null && !executor.isTerminated())
         {
             executor.shutdownNow();
