You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by na...@apache.org on 2017/11/20 10:29:19 UTC

[3/4] fineract git commit: notification sms

notification sms


Project: http://git-wip-us.apache.org/repos/asf/fineract/repo
Commit: http://git-wip-us.apache.org/repos/asf/fineract/commit/8f30c210
Tree: http://git-wip-us.apache.org/repos/asf/fineract/tree/8f30c210
Diff: http://git-wip-us.apache.org/repos/asf/fineract/diff/8f30c210

Branch: refs/heads/develop
Commit: 8f30c2102f9f3526ac274f48e11ab2a5f91a86ff
Parents: d6accae
Author: nazeer shaik <na...@confluxtechnologies.com>
Authored: Thu Nov 2 16:24:28 2017 +0530
Committer: nazeer shaik <na...@confluxtechnologies.com>
Committed: Thu Nov 2 16:24:28 2017 +0530

----------------------------------------------------------------------
 .../campaigns/constants/CampaignType.java       |  12 +-
 .../sms/constants/SmsCampaignEnumerations.java  |   3 +
 .../campaigns/sms/data/SmsCampaignData.java     |  16 +-
 .../campaigns/sms/domain/SmsCampaign.java       |  36 +-
 .../sms/domain/SmsCampaignRepository.java       |   2 +
 .../sms/serialization/SmsCampaignValidator.java |  38 +-
 .../service/SmsCampaignDomainServiceImpl.java   |  23 +-
 ...CampaignDropdownReadPlatformServiceImpl.java |   3 -
 .../SmsCampaignReadPlatformServiceImpl.java     |   7 +-
 .../SmsCampaignWritePlatformServiceJpaImpl.java |  49 +-
 ...ropertiesCommandFromApiJsonDeserializer.java |   6 +
 .../service/ExternalServicesConstants.java      |  36 +
 ...alServicesPropertiesReadPlatformService.java |   3 +
 ...rvicesPropertiesReadPlatformServiceImpl.java |  35 +
 ...ExternalServicesReadPlatformServiceImpl.java |   4 +
 .../infrastructure/gcm/GcmConstants.java        | 242 ++++++
 .../gcm/api/DeviceRegistrationApiConstants.java |  25 +
 .../gcm/api/DeviceRegistrationApiResource.java  | 160 ++++
 .../gcm/domain/DeviceRegistration.java          |  85 ++
 .../gcm/domain/DeviceRegistrationData.java      |  47 ++
 .../domain/DeviceRegistrationRepository.java    |  36 +
 .../DeviceRegistrationRepositoryWrapper.java    |  61 ++
 .../infrastructure/gcm/domain/Message.java      | 317 +++++++
 .../gcm/domain/MulticastResult.java             | 151 ++++
 .../infrastructure/gcm/domain/Notification.java | 330 ++++++++
 .../domain/NotificationConfigurationData.java   |  49 ++
 .../infrastructure/gcm/domain/Result.java       | 187 +++++
 .../infrastructure/gcm/domain/Sender.java       | 832 +++++++++++++++++++
 .../DeviceRegistrationNotFoundException.java    |  38 +
 .../gcm/exception/InvalidRequestException.java  |  66 ++
 .../DeviceRegistrationReadPlatformService.java  |  32 +
 ...viceRegistrationReadPlatformServiceImpl.java | 125 +++
 .../DeviceRegistrationWritePlatformService.java |  30 +
 ...iceRegistrationWritePlatformServiceImpl.java | 123 +++
 .../gcm/service/NotificationSenderService.java  | 133 +++
 .../infrastructure/sms/domain/SmsMessage.java   |  32 +-
 .../sms/domain/SmsMessageAssembler.java         |   4 +-
 .../SmsMessageScheduledJobServiceImpl.java      |  36 +-
 .../portfolio/client/data/ClientData.java       |   6 +
 ...iceRegistrationWritePlatformServiceImpl.java |   3 +-
 .../core_db/V336__sms_campaign_notification.sql |  48 ++
 41 files changed, 3409 insertions(+), 62 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/constants/CampaignType.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/constants/CampaignType.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/constants/CampaignType.java
index b6a0aaf..421882a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/constants/CampaignType.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/constants/CampaignType.java
@@ -21,7 +21,7 @@ package org.apache.fineract.infrastructure.campaigns.constants;
 import org.apache.fineract.infrastructure.core.data.EnumOptionData;
 
 public enum CampaignType {
-    INVALID(0, "campaignType.invalid"), SMS(1, "campaignType.sms");
+    INVALID(0, "campaignType.invalid"), SMS(1, "campaignType.sms"), NOTIFICATION(2, "campaignType.notification");
 
     private Integer value;
     private String code;
@@ -48,6 +48,9 @@ public enum CampaignType {
             case 1:
                 type = SMS;
             break;
+            case 2:
+                type = NOTIFICATION;
+            break;
         }
         return type;
     }
@@ -66,6 +69,9 @@ public enum CampaignType {
             case SMS:
                 optionData = new EnumOptionData(CampaignType.SMS.getValue().longValue(), CampaignType.SMS.getCode(), "SMS");
             break;
+            case NOTIFICATION:
+                optionData = new EnumOptionData(CampaignType.NOTIFICATION.getValue().longValue(), CampaignType.NOTIFICATION.getCode(), "NOTIFICATION");
+            break;
         }
         return optionData;
     }
@@ -73,4 +79,8 @@ public enum CampaignType {
     public boolean isSms() {
         return this.value.equals(CampaignType.SMS.getValue());
     }
+
+    public boolean isNotificaion() {
+        return this.value.equals(CampaignType.NOTIFICATION.getValue());
+    }
 }

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignEnumerations.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignEnumerations.java
index 99de55e..6e74932 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignEnumerations.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/constants/SmsCampaignEnumerations.java
@@ -61,6 +61,9 @@ public class SmsCampaignEnumerations {
             case SMS:
                 optionData = new EnumOptionData(CampaignType.SMS.getValue().longValue(), CampaignType.SMS.getCode(), "SMS");
             break;
+            case NOTIFICATION:
+                optionData = new EnumOptionData(CampaignType.NOTIFICATION.getValue().longValue(), CampaignType.NOTIFICATION.getCode(), "NOTIFICATION");
+            break;
         }
         return optionData;
     }

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsCampaignData.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsCampaignData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsCampaignData.java
index 8a6e6a3..cbd9bd5 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsCampaignData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/data/SmsCampaignData.java
@@ -44,6 +44,8 @@ public class SmsCampaignData {
     private final DateTime recurrenceStartDate;
     private final String recurrence;
     private final Long providerId;
+    private final boolean isNotification;
+    
 
     private final Collection<SmsProviderData> smsProviderOptions;
 
@@ -70,7 +72,7 @@ public class SmsCampaignData {
             final Collection<SmsProviderData> smsProviderOptions, final Collection<EnumOptionData> campaignTypeOptions,
             final Collection<EnumOptionData> triggerTypeOptions, final Collection<EnumOptionData> months, 
             final Collection<EnumOptionData> weekDays, final Collection<EnumOptionData> frequencyTypeOptions, 
-            final Collection<EnumOptionData> periodFrequencyOptions) {
+            final Collection<EnumOptionData> periodFrequencyOptions, final boolean isNotification) {
         this.id = id;
         this.campaignName = campaignName;
         this.campaignType = campaignType;
@@ -90,6 +92,7 @@ public class SmsCampaignData {
         } else {
             this.lastTriggerDate = null;
         }
+        this.isNotification = isNotification;
         this.smsCampaignTimeLine = smsCampaignTimeLine;
         this.recurrenceStartDate = recurrenceStartDate;
         this.recurrence = recurrence;
@@ -109,7 +112,7 @@ public class SmsCampaignData {
             final Long runReportId, final String reportName, final String paramValue, final EnumOptionData campaignStatus,
             final String message, final DateTime nextTriggerDate, final LocalDate lastTriggerDate,
             final SmsCampaignTimeLine smsCampaignTimeLine, final DateTime recurrenceStartDate, final String recurrence,
-            final Long providerId) {
+            final Long providerId, final boolean isNotification) {
         final Collection<SmsBusinessRulesData> businessRulesOptions = null;
         final Collection<SmsProviderData> smsProviderOptions = null;
         final Collection<EnumOptionData> campaignTypeOptions = null;
@@ -122,7 +125,7 @@ public class SmsCampaignData {
         return new SmsCampaignData(id, campaignName, campaignType, triggerType, runReportId,
                 reportName, paramValue, campaignStatus, message, nextTriggerDate, lastTriggerDate, smsCampaignTimeLine,
                 recurrenceStartDate, recurrence, providerId, businessRulesOptions, smsProviderOptions, campaignTypeOptions,
-                triggerTypeOptions, months, weekDays, frequencyTypeOptions, periodFrequencyOptions);
+                triggerTypeOptions, months, weekDays, frequencyTypeOptions, periodFrequencyOptions, isNotification);
     }
 
     public static SmsCampaignData template(final Collection<SmsProviderData> smsProviderOptions,
@@ -145,10 +148,11 @@ public class SmsCampaignData {
         final EnumOptionData triggerType = null;
         final String reportName = null;
         final Long providerId = null;
+        final boolean isNotification = false;
         return new SmsCampaignData(id, campaignName, campaignType, triggerType, runReportId,
                 reportName, paramValue, campaignStatus, message, nextTriggerDate, lastTriggerDate, smsCampaignTimeLine,
                 recurrenceStartDate, recurrence, providerId, businessRulesOptions, smsProviderOptions, campaignTypeOptions,
-                triggerTypeOptions, months, weekDays, frequencyTypeOptions, periodFrequencyOptions);
+                triggerTypeOptions, months, weekDays, frequencyTypeOptions, periodFrequencyOptions, isNotification);
     }
 
     public Long getId() {
@@ -203,4 +207,8 @@ public class SmsCampaignData {
         return this.providerId;
     }
 
+	public boolean isNotification() {
+		return this.isNotification;
+	}
+
 }

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaign.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaign.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaign.java
index 3e1610e..1c023e4 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaign.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaign.java
@@ -67,7 +67,7 @@ public class SmsCampaign extends AbstractPersistableCustom<Long> {
     @Column(name = "campaign_trigger_type", nullable = false)
     private Integer triggerType; //defines direct, scheduled, transaction
     
-    @Column(name = "provider_id", nullable = false)
+    @Column(name = "provider_id", nullable = true)//null for notifications
     private Long providerId; // defined provider details
 
     @ManyToOne
@@ -124,13 +124,16 @@ public class SmsCampaign extends AbstractPersistableCustom<Long> {
 
     @Column(name = "is_visible", nullable = true)
     private boolean isVisible;
+    
+    @Column(name = "is_notification", nullable = true)
+    private boolean isNotification;
 
     public SmsCampaign() {}
 
     private SmsCampaign(final String campaignName, final Integer campaignType, 
             final Integer triggerType, final Report businessRuleId, final Long providerId, final String paramValue,
             final String message, final LocalDate submittedOnDate, final AppUser submittedBy, final String recurrence,
-            final LocalDateTime localDateTime) {
+            final LocalDateTime localDateTime, final boolean isNotification) {
         this.campaignName = campaignName;
         this.campaignType = campaignType;
         this.triggerType = SmsCampaignTriggerType.fromInt(triggerType).getValue();
@@ -149,6 +152,7 @@ public class SmsCampaign extends AbstractPersistableCustom<Long> {
         } else {
             this.recurrenceStartDate = recurrenceStartDate.toDate();
         }
+        this.isNotification = isNotification;
     }
 
     public static SmsCampaign instance(final AppUser submittedBy, final Report report, final JsonCommand command) {
@@ -156,7 +160,15 @@ public class SmsCampaign extends AbstractPersistableCustom<Long> {
         final String campaignName = command.stringValueOfParameterNamed(SmsCampaignValidator.campaignName);
         final Long campaignType = command.longValueOfParameterNamed(SmsCampaignValidator.campaignType);
         final Long triggerType = command.longValueOfParameterNamed(SmsCampaignValidator.triggerType);
-        final Long providerId = command.longValueOfParameterNamed(SmsCampaignValidator.providerId);
+        boolean isNotification = false;
+        if(command.parameterExists(SmsCampaignValidator.isNotificationParamName)){
+        	isNotification = command.booleanPrimitiveValueOfParameterNamed(SmsCampaignValidator.isNotificationParamName);
+        }
+        Long providerId = null;
+        if(!isNotification){
+        	providerId = command.longValueOfParameterNamed(SmsCampaignValidator.providerId);
+        }
+        
         final String paramValue = command.jsonFragment(SmsCampaignValidator.paramValue);
 
         final String message = command.stringValueOfParameterNamed(SmsCampaignValidator.message);
@@ -165,6 +177,7 @@ public class SmsCampaign extends AbstractPersistableCustom<Long> {
             submittedOnDate = command.localDateValueOfParameterNamed(SmsCampaignValidator.submittedOnDateParamName);
         }
         String recurrence = null;
+        
 
         LocalDateTime recurrenceStartDate = new LocalDateTime();
         if (SmsCampaignTriggerType.fromInt(triggerType.intValue()).isSchedule()) {
@@ -184,7 +197,7 @@ public class SmsCampaign extends AbstractPersistableCustom<Long> {
         }
 
         return new SmsCampaign(campaignName, campaignType.intValue(), triggerType.intValue(), report,
-                providerId, paramValue, message, submittedOnDate, submittedBy, recurrence, recurrenceStartDate);
+                providerId, paramValue, message, submittedOnDate, submittedBy, recurrence, recurrenceStartDate, isNotification);
     }
 
     public Map<String, Object> update(JsonCommand command) {
@@ -232,6 +245,11 @@ public class SmsCampaign extends AbstractPersistableCustom<Long> {
             final Long newValue = command.longValueOfParameterNamed(SmsCampaignValidator.providerId);
             actualChanges.put(SmsCampaignValidator.providerId, newValue);
         }
+        if (command.isChangeInBooleanParameterNamed(SmsCampaignValidator.isNotificationParamName, this.isNotification)) {
+            final Boolean newValue = command.booleanObjectValueOfParameterNamed(SmsCampaignValidator.isNotificationParamName);
+            this.isNotification = newValue;
+            actualChanges.put(SmsCampaignValidator.isNotificationParamName, newValue);
+        }
 
         if (SmsCampaignTriggerType.fromInt(this.triggerType).isSchedule()) {
             final String dateFormatAsInput = command.dateFormat();
@@ -558,4 +576,14 @@ public class SmsCampaign extends AbstractPersistableCustom<Long> {
         }
         return recurrenceBuilder.toString();
     }
+
+	public boolean isNotification() {
+		return this.isNotification;
+	}
+
+	public void setNotification(boolean isNotification) {
+		this.isNotification = isNotification;
+	}
+    
+    
 }

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaignRepository.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaignRepository.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaignRepository.java
index bc3bb7d..cb1cf1c 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaignRepository.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/domain/SmsCampaignRepository.java
@@ -32,6 +32,8 @@ public interface SmsCampaignRepository extends JpaRepository<SmsCampaign, Long>,
 
     Collection<SmsCampaign> findByCampaignTypeAndTriggerTypeAndStatus(final Integer campaignType, final Integer triggerType,
             final Integer status);
+    
+    Collection<SmsCampaign> findByTriggerTypeAndStatus(final Integer triggerType, final Integer status);
 
     Collection<SmsCampaign> findByTriggerType(final Integer triggerType) ;
     

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/serialization/SmsCampaignValidator.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/serialization/SmsCampaignValidator.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/serialization/SmsCampaignValidator.java
index dcc9a77..d9eb56f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/serialization/SmsCampaignValidator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/serialization/SmsCampaignValidator.java
@@ -28,12 +28,16 @@ import java.util.Set;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignTriggerType;
+import org.apache.fineract.infrastructure.campaigns.sms.domain.SmsCampaign;
 import org.apache.fineract.infrastructure.core.data.ApiParameterError;
 import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
 import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
 import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
 import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
+import org.apache.fineract.infrastructure.gcm.domain.DeviceRegistration;
+import org.apache.fineract.infrastructure.gcm.domain.DeviceRegistrationRepositoryWrapper;
 import org.apache.fineract.portfolio.calendar.domain.CalendarFrequencyType;
+import org.apache.fineract.portfolio.client.domain.Client;
 import org.joda.time.LocalDate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -67,19 +71,21 @@ public class SmsCampaignValidator {
     public static final String frequencyParamName = "frequency";
     public static final String intervalParamName = "interval";
     public static final String repeatsOnDayParamName = "repeatsOnDay";
+    public static final String isNotificationParamName = "isNotification";
 
     private final FromJsonHelper fromApiJsonHelper;
+    private final DeviceRegistrationRepositoryWrapper deviceRegistrationRepository;
 
     protected static final Set<String> supportedParams = new HashSet<>(Arrays.asList(campaignName, campaignType,
             localeParamName,
             dateFormatParamName, runReportId, paramValue, message, recurrenceStartDate, activationDateParamName, submittedOnDateParamName,
             closureDateParamName, recurrenceParamName, providerId, triggerType, frequencyParamName, intervalParamName,
-            repeatsOnDayParamName, triggerEntityType, triggerActionType, dateTimeFormat));
+            repeatsOnDayParamName, triggerEntityType, triggerActionType, dateTimeFormat, isNotificationParamName));
 
     protected static final Set<String> supportedParamsForUpdate = new HashSet<>(Arrays.asList(campaignName, campaignType,
             localeParamName,
             dateFormatParamName, runReportId, paramValue, message, recurrenceStartDate, activationDateParamName, recurrenceParamName,
-            providerId, triggerType, triggerEntityType, triggerActionType, dateTimeFormat));
+            providerId, triggerType, triggerEntityType, triggerActionType, dateTimeFormat, isNotificationParamName));
 
     protected static final Set<String> ACTIVATION_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(localeParamName,
             dateFormatParamName,
@@ -92,8 +98,9 @@ public class SmsCampaignValidator {
     protected static final Set<String> PREVIEW_REQUEST_DATA_PARAMETERS = new HashSet<>(Arrays.asList(paramValue, message));
 
     @Autowired
-    public SmsCampaignValidator(FromJsonHelper fromApiJsonHelper) {
+    public SmsCampaignValidator(FromJsonHelper fromApiJsonHelper, final DeviceRegistrationRepositoryWrapper deviceRegistrationRepository) {
         this.fromApiJsonHelper = fromApiJsonHelper;
+        this.deviceRegistrationRepository = deviceRegistrationRepository;
     }
 
     public void validateCreate(String json) {
@@ -159,6 +166,12 @@ public class SmsCampaignValidator {
                     element);
             baseDataValidator.reset().parameter(SmsCampaignValidator.submittedOnDateParamName).value(submittedOnDate).notNull();
         }
+        
+        if (this.fromApiJsonHelper.parameterExists(SmsCampaignValidator.isNotificationParamName, element)) {
+            final Boolean isNotification = this.fromApiJsonHelper.extractBooleanNamed(SmsCampaignValidator.isNotificationParamName,
+                    element);
+            baseDataValidator.reset().parameter(SmsCampaignValidator.submittedOnDateParamName).trueOrFalseRequired(isNotification);
+        }
         throwExceptionIfValidationWarningsExist(dataValidationErrors);
 
     }
@@ -214,7 +227,11 @@ public class SmsCampaignValidator {
                 }
             }
         }
-
+        if (this.fromApiJsonHelper.parameterExists(SmsCampaignValidator.isNotificationParamName, element)) {
+            final Boolean isNotification = this.fromApiJsonHelper.extractBooleanNamed(SmsCampaignValidator.isNotificationParamName,
+                    element);
+            baseDataValidator.reset().parameter(SmsCampaignValidator.submittedOnDateParamName).trueOrFalseRequired(isNotification);
+        }
         throwExceptionIfValidationWarningsExist(dataValidationErrors);
 
     }
@@ -303,4 +320,17 @@ public class SmsCampaignValidator {
     private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
         if (!dataValidationErrors.isEmpty()) { throw new PlatformApiDataValidationException(dataValidationErrors); }
     }
+    
+	public boolean isValidNotificationOrSms(Client client, SmsCampaign smsCampaign, Object mobileNo) {
+		if (smsCampaign.isNotification()) {
+			if (client != null) {
+				DeviceRegistration deviceRegistration = this.deviceRegistrationRepository
+						.findDeviceRegistrationByClientId(client.getId());
+				return (deviceRegistration != null);
+			}
+			return false;
+		}
+		return (mobileNo != null);
+	}
+    
 }

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignDomainServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignDomainServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignDomainServiceImpl.java
index 3b0e0ae..7bf79ad 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignDomainServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignDomainServiceImpl.java
@@ -35,6 +35,7 @@ import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignTri
 import org.apache.fineract.infrastructure.campaigns.sms.domain.SmsCampaign;
 import org.apache.fineract.infrastructure.campaigns.sms.domain.SmsCampaignRepository;
 import org.apache.fineract.infrastructure.campaigns.sms.exception.SmsRuntimeException;
+import org.apache.fineract.infrastructure.campaigns.sms.serialization.SmsCampaignValidator;
 import org.apache.fineract.infrastructure.sms.domain.SmsMessage;
 import org.apache.fineract.infrastructure.sms.domain.SmsMessageRepository;
 import org.apache.fineract.infrastructure.sms.scheduler.SmsMessageScheduledJobService;
@@ -77,13 +78,15 @@ public class SmsCampaignDomainServiceImpl implements SmsCampaignDomainService {
     private final GroupRepository groupRepository;
 
     private final SmsMessageScheduledJobService smsMessageScheduledJobService; 
+    private final SmsCampaignValidator smsCampaignValidator;
     
     @Autowired
     public SmsCampaignDomainServiceImpl(final SmsCampaignRepository smsCampaignRepository, final SmsMessageRepository smsMessageRepository,
                                         final BusinessEventNotifierService businessEventNotifierService, final OfficeRepository officeRepository,
                                         final SmsCampaignWritePlatformService smsCampaignWritePlatformCommandHandler,
                                         final GroupRepository groupRepository,
-                                        final SmsMessageScheduledJobService smsMessageScheduledJobService){
+                                        final SmsMessageScheduledJobService smsMessageScheduledJobService,
+                                        final SmsCampaignValidator smsCampaignValidator){
         this.smsCampaignRepository = smsCampaignRepository;
         this.smsMessageRepository = smsMessageRepository;
         this.businessEventNotifierService = businessEventNotifierService;
@@ -91,6 +94,7 @@ public class SmsCampaignDomainServiceImpl implements SmsCampaignDomainService {
         this.smsCampaignWritePlatformCommandHandler = smsCampaignWritePlatformCommandHandler;
         this.groupRepository = groupRepository;
         this.smsMessageScheduledJobService = smsMessageScheduledJobService ;
+        this.smsCampaignValidator = smsCampaignValidator;
     }
 
     @PostConstruct
@@ -217,10 +221,13 @@ public class SmsCampaignDomainServiceImpl implements SmsCampaignDomainService {
 							String message = this.smsCampaignWritePlatformCommandHandler.compileSmsTemplate(
 									smsCampaign.getMessage(), smsCampaign.getCampaignName(), smsParams);
 							Object mobileNo = smsParams.get("mobileNo");
-							if (mobileNo != null) {
+							if (this.smsCampaignValidator.isValidNotificationOrSms(client, smsCampaign, mobileNo)) {
+								String mobileNumber = null;
+		                    	if(mobileNo != null){
+		                    		mobileNumber = mobileNo.toString();
+		                    	}
 								SmsMessage smsMessage = SmsMessage.pendingSms(null, null, client, null, message,
-										mobileNo.toString(), smsCampaign);
-								this.smsMessageRepository.save(smsMessage);
+										mobileNumber, smsCampaign, smsCampaign.isNotification());
 								Collection<SmsMessage> messages = new ArrayList<>();
 								messages.add(smsMessage);
 								Map<SmsCampaign, Collection<SmsMessage>> smsDataMap = new HashMap<>();
@@ -273,9 +280,13 @@ public class SmsCampaignDomainServiceImpl implements SmsCampaignDomainService {
 					String message = this.smsCampaignWritePlatformCommandHandler
 							.compileSmsTemplate(smsCampaign.getMessage(), smsCampaign.getCampaignName(), smsParams);
 					Object mobileNo = smsParams.get("mobileNo");
-					if (mobileNo != null) {
+					if (this.smsCampaignValidator.isValidNotificationOrSms(client, smsCampaign, mobileNo)) {
+						String mobileNumber = null;
+                    	if(mobileNo != null){
+                    		mobileNumber = mobileNo.toString();
+                    	}
 						SmsMessage smsMessage = SmsMessage.pendingSms(null, null, client, null, message,
-								mobileNo.toString(), smsCampaign);
+								mobileNumber, smsCampaign, smsCampaign.isNotification());
 						this.smsMessageRepository.save(smsMessage);
 						Collection<SmsMessage> messages = new ArrayList<>();
 						messages.add(smsMessage);

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignDropdownReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignDropdownReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignDropdownReadPlatformServiceImpl.java
index d974d7a..69536f8 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignDropdownReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignDropdownReadPlatformServiceImpl.java
@@ -30,7 +30,6 @@ import org.apache.fineract.infrastructure.campaigns.helper.SmsConfigUtils;
 import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignEnumerations;
 import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignTriggerType;
 import org.apache.fineract.infrastructure.campaigns.sms.data.SmsProviderData;
-import org.apache.fineract.infrastructure.campaigns.sms.exception.ConnectionFailureException;
 import org.apache.fineract.infrastructure.core.data.EnumOptionData;
 import org.apache.fineract.portfolio.calendar.domain.CalendarWeekDaysType;
 import org.apache.fineract.portfolio.calendar.service.CalendarEnumerations;
@@ -81,10 +80,8 @@ public class SmsCampaignDropdownReadPlatformServiceImpl implements SmsCampaignDr
                     new ParameterizedTypeReference<Collection<SmsProviderData>>() {});
             smsProviderOptions = responseOne.getBody();
             if (!responseOne.getStatusCode().equals(HttpStatus.OK)) {
-                throw new ConnectionFailureException(hostName);
             }
         } catch (Exception e) {
-        	 throw new ConnectionFailureException(hostName);
         }
         return smsProviderOptions;
     }

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignReadPlatformServiceImpl.java
index 94215b0..5e15272 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignReadPlatformServiceImpl.java
@@ -219,7 +219,8 @@ public class SmsCampaignReadPlatformServiceImpl implements SmsCampaignReadPlatfo
             sql.append("acu.username as activatedByUsername, ");
             sql.append("sc.approvedon_date as activatedOnDate, ");
             sql.append("sr.report_name as reportName, ");
-            sql.append("provider_id as providerId ");
+            sql.append("provider_id as providerId, ");
+            sql.append("sc.is_notification as isNotification ");
             sql.append("from sms_campaign sc ");
             sql.append("left join m_appuser sbu on sbu.id = sc.submittedon_userid ");
             sql.append("left join m_appuser acu on acu.id = sc.approvedon_userid ");
@@ -265,9 +266,9 @@ public class SmsCampaignReadPlatformServiceImpl implements SmsCampaignReadPlatfo
                     activatedByUsername, closedOnDate, closedByUsername);
             final String reportName = rs.getString("reportName");
             final Long providerId = rs.getLong("providerId");
-
+            final Boolean isNotification = rs.getBoolean("isNotification");
             return SmsCampaignData.instance(id, campaignName, campaignTypeEnum, triggerTypeEnum, runReportId, reportName, paramValue, status, message, nextTriggerDate, lastTriggerDate,
-                    smsCampaignTimeLine, recurrenceStartDate, recurrence, providerId);
+                    smsCampaignTimeLine, recurrenceStartDate, recurrence, providerId, isNotification);
         }
     }
 

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java
index 0084ed6..976a6d7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/campaigns/sms/service/SmsCampaignWritePlatformServiceJpaImpl.java
@@ -30,7 +30,6 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
-import org.apache.fineract.infrastructure.campaigns.constants.CampaignType;
 import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignStatus;
 import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignTriggerType;
 import org.apache.fineract.infrastructure.campaigns.sms.data.CampaignPreviewData;
@@ -56,6 +55,8 @@ import org.apache.fineract.infrastructure.dataqueries.domain.ReportRepository;
 import org.apache.fineract.infrastructure.dataqueries.exception.ReportNotFoundException;
 import org.apache.fineract.infrastructure.dataqueries.service.GenericDataService;
 import org.apache.fineract.infrastructure.dataqueries.service.ReadReportingService;
+import org.apache.fineract.infrastructure.gcm.domain.DeviceRegistration;
+import org.apache.fineract.infrastructure.gcm.domain.DeviceRegistrationRepositoryWrapper;
 import org.apache.fineract.infrastructure.jobs.annotation.CronTarget;
 import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
 import org.apache.fineract.infrastructure.jobs.service.JobName;
@@ -109,6 +110,7 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
     private final ReadReportingService readReportingService;
     private final GenericDataService genericDataService;
     private final FromJsonHelper fromJsonHelper;
+    private final DeviceRegistrationRepositoryWrapper deviceRegistrationRepository;
 
     private final SmsMessageScheduledJobService smsMessageScheduledJobService;
     
@@ -118,7 +120,7 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
             final SmsMessageRepository smsMessageRepository, final ClientRepositoryWrapper clientRepositoryWrapper,
             final ReadReportingService readReportingService, final GenericDataService genericDataService,
             final FromJsonHelper fromJsonHelper, final GroupRepository groupRepository,
-            final SmsMessageScheduledJobService smsMessageScheduledJobService) {
+            final SmsMessageScheduledJobService smsMessageScheduledJobService, final DeviceRegistrationRepositoryWrapper deviceRegistrationRepository) {
         this.context = context;
         this.smsCampaignRepository = smsCampaignRepository;
         this.smsCampaignValidator = smsCampaignValidator;
@@ -130,6 +132,7 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
         this.fromJsonHelper = fromJsonHelper;
         this.groupRepository = groupRepository;
         this.smsMessageScheduledJobService = smsMessageScheduledJobService ;
+        this.deviceRegistrationRepository = deviceRegistrationRepository;
     }
 
     @Transactional
@@ -228,10 +231,14 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
                     Object mobileNo = entry.get("mobileNo");
 
                     Client client = this.clientRepositoryWrapper.findOneWithNotFoundDetection(clientId.longValue());
-                    if (mobileNo != null) {
+                    if (this.smsCampaignValidator.isValidNotificationOrSms(client, smsCampaign, mobileNo)) {
 //                        String countryCode = this.smsReadPlatformService.retrieveCountryCode(client.getOffice().getId()).getCountryCode();
-                        SmsMessage smsMessage = SmsMessage.pendingSms(null, null, client, null, textMessage, mobileNo.toString(),
-                                smsCampaign);
+                    	String mobileNumber = null;
+                    	if(mobileNo != null){
+                    		mobileNumber = mobileNo.toString();
+                    	}
+                        SmsMessage smsMessage = SmsMessage.pendingSms(null, null, client, null, textMessage, mobileNumber,
+                                smsCampaign, smsCampaign.isNotification());
                         this.smsMessageRepository.save(smsMessage);
                     }
                 }
@@ -242,6 +249,8 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
 
     }
 
+
+
     @Override
     public void insertDirectCampaignIntoSmsOutboundTable(final Loan loan, final SmsCampaign smsCampaign) {
         try {
@@ -278,9 +287,13 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
                         String textMessage = this.compileSmsTemplate(smsCampaign.getMessage(), smsCampaign.getCampaignName(), entry);
                         Object mobileNo = entry.get("mobileNo");
                         
-                        if (mobileNo != null) {
-                            SmsMessage smsMessage = SmsMessage.pendingSms(null, null, client, null, textMessage, mobileNo.toString(),
-                                    smsCampaign);
+                        if (this.smsCampaignValidator.isValidNotificationOrSms(client, smsCampaign, mobileNo)) {
+                        	String mobileNumber = null;
+                        	if(mobileNo != null){
+                        		mobileNumber = mobileNo.toString();
+                        	}
+                            SmsMessage smsMessage = SmsMessage.pendingSms(null, null, client, null, textMessage, mobileNumber,
+                                    smsCampaign, smsCampaign.isNotification());
                             smsMessage.setStatusType(SmsMessageStatusType.WAITING_FOR_DELIVERY_REPORT.getValue());
                             this.smsMessageRepository.save(smsMessage);
                             Collection<SmsMessage> messages = new ArrayList<>() ;
@@ -320,9 +333,13 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
 							smsCampaign.getCampaignName(), entry);
 					Object mobileNo = entry.get("mobileNo");
 
-					if (mobileNo != null) {
+					if (this.smsCampaignValidator.isValidNotificationOrSms(client, smsCampaign, mobileNo)) {
+						String mobileNumber = null;
+                    	if(mobileNo != null){
+                    		mobileNumber = mobileNo.toString();
+                    	}
 						SmsMessage smsMessage = SmsMessage.pendingSms(null, null, client, null, textMessage,
-								mobileNo.toString(), smsCampaign);
+								mobileNumber, smsCampaign, smsCampaign.isNotification());
 						smsMessage.setStatusType(SmsMessageStatusType.WAITING_FOR_DELIVERY_REPORT.getValue());
 						this.smsMessageRepository.save(smsMessage);
 						Collection<SmsMessage> messages = new ArrayList<>();
@@ -360,9 +377,13 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
 							smsCampaign.getCampaignName(), entry);
 					Object mobileNo = entry.get("mobileNo");
 
-					if (mobileNo != null) {
+					if (this.smsCampaignValidator.isValidNotificationOrSms(client, smsCampaign, mobileNo)) {
+						String mobileNumber = null;
+                    	if(mobileNo != null){
+                    		mobileNumber = mobileNo.toString();
+                    	}
 						SmsMessage smsMessage = SmsMessage.pendingSms(null, null, client, null, textMessage,
-								mobileNo.toString(), smsCampaign);
+								mobileNumber, smsCampaign, smsCampaign.isNotification());
 						smsMessage.setStatusType(SmsMessageStatusType.WAITING_FOR_DELIVERY_REPORT.getValue());
 						this.smsMessageRepository.save(smsMessage);
 						Collection<SmsMessage> messages = new ArrayList<>();
@@ -634,8 +655,8 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
     @Override
     @CronTarget(jobName = JobName.UPDATE_SMS_OUTBOUND_WITH_CAMPAIGN_MESSAGE)
     public void storeTemplateMessageIntoSmsOutBoundTable() throws JobExecutionException {
-        final Collection<SmsCampaign> smsCampaignDataCollection = this.smsCampaignRepository.findByCampaignTypeAndTriggerTypeAndStatus(
-                CampaignType.SMS.getValue(), SmsCampaignTriggerType.SCHEDULE.getValue(), SmsCampaignStatus.ACTIVE.getValue());
+        final Collection<SmsCampaign> smsCampaignDataCollection = this.smsCampaignRepository.findByTriggerTypeAndStatus(
+                SmsCampaignTriggerType.SCHEDULE.getValue(), SmsCampaignStatus.ACTIVE.getValue());
         if (smsCampaignDataCollection != null) {
             for (SmsCampaign smsCampaign : smsCampaignDataCollection) {
                 LocalDateTime tenantDateNow = tenantDateTime();

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/serialization/ExternalServicesPropertiesCommandFromApiJsonDeserializer.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/serialization/ExternalServicesPropertiesCommandFromApiJsonDeserializer.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/serialization/ExternalServicesPropertiesCommandFromApiJsonDeserializer.java
index 9b5aeb2..3168ef4 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/serialization/ExternalServicesPropertiesCommandFromApiJsonDeserializer.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/serialization/ExternalServicesPropertiesCommandFromApiJsonDeserializer.java
@@ -24,6 +24,7 @@ import java.util.Set;
 
 import org.apache.commons.lang.StringUtils;
 import org.apache.fineract.infrastructure.configuration.exception.ExternalServiceConfigurationNotFoundException;
+import org.apache.fineract.infrastructure.configuration.service.ExternalServicesConstants.NOTIFICATION_JSON_INPUT_PARAMS;
 import org.apache.fineract.infrastructure.configuration.service.ExternalServicesConstants.S3_JSON_INPUT_PARAMS;
 import org.apache.fineract.infrastructure.configuration.service.ExternalServicesConstants.SMS_JSON_INPUT_PARAMS;
 import org.apache.fineract.infrastructure.configuration.service.ExternalServicesConstants.SMTP_JSON_INPUT_PARAMS;
@@ -40,6 +41,7 @@ public class ExternalServicesPropertiesCommandFromApiJsonDeserializer {
     private final Set<String> S3SupportedParameters = S3_JSON_INPUT_PARAMS.getAllValues();
     private final Set<String> SMTPSupportedParameters = SMTP_JSON_INPUT_PARAMS.getAllValues();
     private final Set<String> SMSSupportedParameters = SMS_JSON_INPUT_PARAMS.getAllValues();
+    private final Set<String> NotificationSupportedParameters = NOTIFICATION_JSON_INPUT_PARAMS.getAllValues();
     private final FromJsonHelper fromApiJsonHelper;
 
     @Autowired
@@ -64,6 +66,10 @@ public class ExternalServicesPropertiesCommandFromApiJsonDeserializer {
                 this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.SMSSupportedParameters);
             break;
 
+            case "NOTIFICATION":
+                this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, this.NotificationSupportedParameters);
+            break;
+
             default:
                 throw new ExternalServiceConfigurationNotFoundException(externalServiceName);
         }

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesConstants.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesConstants.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesConstants.java
index 44a56ca..f123c19 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesConstants.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesConstants.java
@@ -41,6 +41,11 @@ public class ExternalServicesConstants {
     public static final String SMS_END_POINT = "end_point";
     public static final String SMS_TENANT_APP_KEY = "tenant_app_key";
     
+    public static final String NOTIFICATION_SERVICE_NAME = "NOTIFICATION";
+    public static final String NOTIFICATION_SERVER_KEY = "server_key";
+    public static final String NOTIFICATION_GCM_END_POINT = "gcm_end_point";
+    public static final String NOTIFICATION_FCM_END_POINT = "fcm_end_point";
+    
     public static enum EXTERNALSERVICEPROPERTIES_JSON_INPUT_PARAMS {
         EXTERNAL_SERVICE_ID("external_service_id"), NAME("name"), VALUE("value");
 
@@ -164,5 +169,36 @@ public class ExternalServicesConstants {
             return this.value;
         }
     }
+    
+    public static enum NOTIFICATION_JSON_INPUT_PARAMS {
+        SERVER_KEY("server_key"), GCM_END_POINT("gcm_end_point"), FCM_END_POINT("fcm_end_point");
+
+        private final String value;
+
+        private NOTIFICATION_JSON_INPUT_PARAMS(final String value) {
+            this.value = value;
+        }
+
+        private static final Set<String> values = new HashSet<>();
+
+        static {
+            for (final NOTIFICATION_JSON_INPUT_PARAMS type : NOTIFICATION_JSON_INPUT_PARAMS.values()) {
+                values.add(type.value);
+            }
+        }
+
+        public static Set<String> getAllValues() {
+            return values;
+        }
+
+        @Override
+        public String toString() {
+            return name().toString().replaceAll("_", " ");
+        }
+
+        public String getValue() {
+            return this.value;
+        }
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformService.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformService.java
index 2d46835..25f4b8e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformService.java
@@ -24,6 +24,7 @@ import org.apache.fineract.infrastructure.campaigns.sms.data.MessageGatewayConfi
 import org.apache.fineract.infrastructure.configuration.data.ExternalServicesPropertiesData;
 import org.apache.fineract.infrastructure.configuration.data.S3CredentialsData;
 import org.apache.fineract.infrastructure.configuration.data.SMTPCredentialsData;
+import org.apache.fineract.infrastructure.gcm.domain.NotificationConfigurationData;
 
 public interface ExternalServicesPropertiesReadPlatformService {
 
@@ -34,5 +35,7 @@ public interface ExternalServicesPropertiesReadPlatformService {
     MessageGatewayConfigurationData getSMSGateway();
 
     Collection<ExternalServicesPropertiesData> retrieveOne(String serviceName);
+    
+    NotificationConfigurationData getNotificationConfiguration();
 
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java
index ccd4012..bf62708 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesPropertiesReadPlatformServiceImpl.java
@@ -28,6 +28,7 @@ import org.apache.fineract.infrastructure.configuration.data.S3CredentialsData;
 import org.apache.fineract.infrastructure.configuration.data.SMTPCredentialsData;
 import org.apache.fineract.infrastructure.configuration.exception.ExternalServiceConfigurationNotFoundException;
 import org.apache.fineract.infrastructure.core.service.RoutingDataSource;
+import org.apache.fineract.infrastructure.gcm.domain.NotificationConfigurationData;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DataAccessException;
 import org.springframework.jdbc.core.JdbcTemplate;
@@ -176,6 +177,10 @@ public class ExternalServicesPropertiesReadPlatformServiceImpl implements Extern
                 serviceNameToUse = ExternalServicesConstants.SMS_SERVICE_NAME;
             break;
 
+            case "NOTIFICATION":
+                serviceNameToUse = ExternalServicesConstants.NOTIFICATION_SERVICE_NAME;
+            break;
+
             default:
                 throw new ExternalServiceConfigurationNotFoundException(serviceName);
         }
@@ -185,5 +190,35 @@ public class ExternalServicesPropertiesReadPlatformServiceImpl implements Extern
         return this.jdbcTemplate.query(sql, mapper, new Object[] {});
 
     }
+    
+    private static final class NotificationDataExtractor implements ResultSetExtractor<NotificationConfigurationData> {
+
+        @Override
+        public NotificationConfigurationData extractData(final ResultSet rs) throws SQLException, DataAccessException {
+            String serverKey = null;
+            String gcmEndPoint = null;
+            String fcmEndPoint = null;
+            while (rs.next()) {
+                if (rs.getString("name").equalsIgnoreCase(ExternalServicesConstants.NOTIFICATION_SERVER_KEY )) {
+                	serverKey = rs.getString("value");
+                } else if (rs.getString("name").equalsIgnoreCase(ExternalServicesConstants.NOTIFICATION_GCM_END_POINT )) {
+                	gcmEndPoint = rs.getString("value");
+                } else if (rs.getString("name").equalsIgnoreCase(ExternalServicesConstants.NOTIFICATION_FCM_END_POINT )) {
+                	fcmEndPoint = rs.getString("value");
+                }
+            }
+            return new NotificationConfigurationData(null, serverKey, gcmEndPoint, fcmEndPoint);
+        }
+    }
+
+
+	@Override
+	public NotificationConfigurationData getNotificationConfiguration() {
+		final ResultSetExtractor<NotificationConfigurationData> resultSetExtractor = new NotificationDataExtractor();
+        final String sql = "SELECT esp.name, esp.value FROM c_external_service_properties esp inner join c_external_service es on esp.external_service_id = es.id where es.name = '"
+                + ExternalServicesConstants.NOTIFICATION_SERVICE_NAME + "'";
+        final NotificationConfigurationData notificationConfigurationData = this.jdbcTemplate.query(sql, resultSetExtractor, new Object[] {});
+        return notificationConfigurationData;
+	}
 
 }

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesReadPlatformServiceImpl.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesReadPlatformServiceImpl.java
index 44f4164..9bab493 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/configuration/service/ExternalServicesReadPlatformServiceImpl.java
@@ -58,6 +58,10 @@ public class ExternalServicesReadPlatformServiceImpl implements ExternalServices
                 serviceNameToUse = ExternalServicesConstants.SMS_SERVICE_NAME;
             break;
 
+            case "NOTIFICATION":
+                serviceNameToUse = ExternalServicesConstants.NOTIFICATION_SERVICE_NAME;
+            break;
+
             default:
                 throw new ExternalServiceConfigurationNotFoundException(serviceName);
         }

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/GcmConstants.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/GcmConstants.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/GcmConstants.java
new file mode 100644
index 0000000..fa3b9bb
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/GcmConstants.java
@@ -0,0 +1,242 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.gcm;
+
+/**
+ * Constants used on GCM service communication.
+ */
+public final class GcmConstants {
+	
+	
+	/**
+	 * Title for notification
+	 */	
+	public static final String title = "Hello !";
+	
+	/**
+	 * icon for notification.
+	 */
+	public static final String defaultIcon = "default";
+
+	
+	/**
+	 * Parameter for to field.
+	 */
+	public static final String PARAM_TO = "to";
+
+	/**
+	 * Prefix of the topic.
+	 */
+	public static final String TOPIC_PREFIX = "/topics/";
+
+	/**
+	 * HTTP parameter for registration id.
+	 */
+	//public static final String PARAM_REGISTRATION_ID = "registration_id";
+
+	/**
+	 * HTTP parameter for collapse key.
+	 */
+	public static final String PARAM_COLLAPSE_KEY = "collapse_key";
+
+	/**
+	 * HTTP parameter for delaying the message delivery if the device is idle.
+	 */
+	public static final String PARAM_DELAY_WHILE_IDLE = "delay_while_idle";
+
+	/**
+	 * HTTP parameter for telling gcm to validate the message without actually
+	 * sending it.
+	 */
+	public static final String PARAM_DRY_RUN = "dry_run";
+
+	/**
+	 * HTTP parameter for package name that can be used to restrict message
+	 * delivery by matching against the package name used to generate the
+	 * registration id.
+	 */
+	public static final String PARAM_RESTRICTED_PACKAGE_NAME = "restricted_package_name";
+
+	/**
+	 * Parameter used to set the message time-to-live.
+	 */
+	public static final String PARAM_TIME_TO_LIVE = "time_to_live";
+
+	/**
+	 * Parameter used to set the message priority.
+	 */
+	public static final String PARAM_PRIORITY = "priority";
+
+	/**
+	 * Parameter used to set the content available (iOS only)
+	 */
+	public static final String PARAM_CONTENT_AVAILABLE = "content_available";
+
+	/**
+	 * Value used to set message priority to normal.
+	 */
+	public static final String MESSAGE_PRIORITY_NORMAL = "normal";
+
+	/**
+	 * Value used to set message priority to high.
+	 */
+	public static final String MESSAGE_PRIORITY_HIGH = "high";
+	
+	/**
+	 * A particular message could not be sent because the GCM servers were not
+	 * available. Used only on JSON requests, as in plain text requests
+	 * unavailability is indicated by a 503 response.
+	 */
+	public static final String ERROR_UNAVAILABLE = "Unavailable";
+
+	/**
+	 * A particular message could not be sent because the GCM servers
+	 * encountered an error. Used only on JSON requests, as in plain text
+	 * requests internal errors are indicated by a 500 response.
+	 */
+	public static final String ERROR_INTERNAL_SERVER_ERROR = "InternalServerError";
+
+	/**
+	 * Token returned by GCM when the requested registration id has a canonical
+	 * value.
+	 */
+	public static final String TOKEN_CANONICAL_REG_ID = "registration_id";
+
+	/**
+	 * JSON-only field representing the registration ids.
+	 */
+	public static final String JSON_REGISTRATION_IDS = "registration_ids";
+
+	/**
+	 * JSON-only field representing the to recipient.
+	 */
+	public static final String JSON_TO = "to";
+
+	/**
+	 * JSON-only field representing the payload data.
+	 */
+	public static final String JSON_PAYLOAD = "data";
+
+	/**
+	 * JSON-only field representing the notification payload.
+	 */
+	public static final String JSON_NOTIFICATION = "notification";
+
+	/**
+	 * JSON-only field representing the notification title.
+	 */
+	public static final String JSON_NOTIFICATION_TITLE = "title";
+
+	/**
+	 * JSON-only field representing the notification body.
+	 */
+	public static final String JSON_NOTIFICATION_BODY = "body";
+
+	/**
+	 * JSON-only field representing the notification icon.
+	 */
+	public static final String JSON_NOTIFICATION_ICON = "icon";
+
+	/**
+	 * JSON-only field representing the notification sound.
+	 */
+	public static final String JSON_NOTIFICATION_SOUND = "sound";
+
+	/**
+	 * JSON-only field representing the notification badge.
+	 */
+	public static final String JSON_NOTIFICATION_BADGE = "badge";
+
+	/**
+	 * JSON-only field representing the notification tag.
+	 */
+	public static final String JSON_NOTIFICATION_TAG = "tag";
+
+	/**
+	 * JSON-only field representing the notification color.
+	 */
+	public static final String JSON_NOTIFICATION_COLOR = "color";
+
+	/**
+	 * JSON-only field representing the notification click action.
+	 */
+	public static final String JSON_NOTIFICATION_CLICK_ACTION = "click_action";
+
+	/**
+	 * JSON-only field representing the notification body localization key.
+	 */
+	public static final String JSON_NOTIFICATION_BODY_LOC_KEY = "body_loc_key";
+
+	/**
+	 * JSON-only field representing the notification body localization values.
+	 */
+	public static final String JSON_NOTIFICATION_BODY_LOC_ARGS = "body_loc_args";
+
+	/**
+	 * JSON-only field representing the notification title localization key.
+	 */
+	public static final String JSON_NOTIFICATION_TITLE_LOC_KEY = "title_loc_key";
+
+	/**
+	 * JSON-only field representing the notification title localization values.
+	 */
+	public static final String JSON_NOTIFICATION_TITLE_LOC_ARGS = "title_loc_args";
+
+	/**
+	 * JSON-only field representing the number of successful messages.
+	 */
+	public static final String JSON_SUCCESS = "success";
+
+	/**
+	 * JSON-only field representing the number of failed messages.
+	 */
+	public static final String JSON_FAILURE = "failure";
+
+	/**
+	 * JSON-only field representing the number of messages with a canonical
+	 * registration id.
+	 */
+	public static final String JSON_CANONICAL_IDS = "canonical_ids";
+
+	/**
+	 * JSON-only field representing the id of the multicast request.
+	 */
+	public static final String JSON_MULTICAST_ID = "multicast_id";
+
+	/**
+	 * JSON-only field representing the result of each individual request.
+	 */
+	public static final String JSON_RESULTS = "results";
+
+	/**
+	 * JSON-only field representing the error field of an individual request.
+	 */
+	public static final String JSON_ERROR = "error";
+
+	/**
+	 * JSON-only field sent by GCM when a message was successfully sent.
+	 */
+	public static final String JSON_MESSAGE_ID = "message_id";
+
+	private GcmConstants() {
+		throw new UnsupportedOperationException();
+	}
+	
+	public static final Integer TIME_TO_LIVE = 30;
+}

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/api/DeviceRegistrationApiConstants.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/api/DeviceRegistrationApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/api/DeviceRegistrationApiConstants.java
new file mode 100644
index 0000000..0b675cb
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/api/DeviceRegistrationApiConstants.java
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.gcm.api;
+
+public class DeviceRegistrationApiConstants {
+	public static final String clientIdParamName = "clientId";
+	public static final String registrationIdParamName = "registrationId";
+
+}

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/api/DeviceRegistrationApiResource.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/api/DeviceRegistrationApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/api/DeviceRegistrationApiResource.java
new file mode 100644
index 0000000..1ba0fa7
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/api/DeviceRegistrationApiResource.java
@@ -0,0 +1,160 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.gcm.api;
+
+import java.util.Collection;
+import java.util.HashMap;
+
+import javax.ws.rs.Consumes;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.UriInfo;
+
+import org.apache.fineract.infrastructure.core.serialization.DefaultToApiJsonSerializer;
+import org.apache.fineract.infrastructure.gcm.domain.DeviceRegistration;
+import org.apache.fineract.infrastructure.gcm.domain.DeviceRegistrationData;
+import org.apache.fineract.infrastructure.gcm.service.DeviceRegistrationReadPlatformService;
+import org.apache.fineract.infrastructure.gcm.service.DeviceRegistrationWritePlatformService;
+import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+
+@Path("/device/registration")
+@Component
+@Scope("singleton")
+public class DeviceRegistrationApiResource {
+
+    private final PlatformSecurityContext context;
+    private final DeviceRegistrationWritePlatformService deviceRegistrationWritePlatformService;
+    private final DefaultToApiJsonSerializer<DeviceRegistrationData> toApiJsonSerializer;
+    private final DeviceRegistrationReadPlatformService deviceRegistrationReadPlatformService;
+
+    @Autowired
+    public DeviceRegistrationApiResource(PlatformSecurityContext context,
+            final DefaultToApiJsonSerializer<DeviceRegistrationData> toApiJsonSerializer,
+            final DeviceRegistrationReadPlatformService deviceRegistrationReadPlatformService,
+            final DeviceRegistrationWritePlatformService deviceRegistrationWritePlatformService) {
+        this.context = context;
+        this.toApiJsonSerializer = toApiJsonSerializer;
+        this.deviceRegistrationReadPlatformService = deviceRegistrationReadPlatformService;
+        this.deviceRegistrationWritePlatformService = deviceRegistrationWritePlatformService;
+    }
+
+    @POST
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String registerDevice(final String apiRequestBodyAsJson) {
+        this.context.authenticatedUser();
+        Gson gson = new Gson();
+        JsonObject json = new Gson().fromJson(apiRequestBodyAsJson, JsonObject.class);
+        Long clientId = json.get(DeviceRegistrationApiConstants.clientIdParamName).getAsLong();
+        String registrationId = json.get(DeviceRegistrationApiConstants.registrationIdParamName).getAsString();
+        DeviceRegistration deviceRegistration = this.deviceRegistrationWritePlatformService.registerDevice(clientId, registrationId);
+        String response = gson.toJson(deviceRegistration);
+        return response;
+    }
+
+    @GET
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String retrieveAllDeviceRegistrations(@Context final UriInfo uriInfo) {
+
+        this.context.authenticatedUser();
+
+        Collection<DeviceRegistrationData> deviceRegistrationDataList = this.deviceRegistrationReadPlatformService
+                .retrieveAllDeviceRegiistrations();
+
+        return this.toApiJsonSerializer.serialize(deviceRegistrationDataList);
+    }
+
+    @GET
+    @Path("client/{clientId}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String retrieveDeviceRegistrationByClientId(@PathParam("clientId") final Long clientId, @Context final UriInfo uriInfo) {
+
+        this.context.authenticatedUser();
+
+        DeviceRegistrationData deviceRegistrationData = this.deviceRegistrationReadPlatformService
+                .retrieveDeviceRegiistrationByClientId(clientId);
+
+        return this.toApiJsonSerializer.serialize(deviceRegistrationData);
+    }
+
+    @GET
+    @Path("{id}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String retrieveDeviceRegiistration(@PathParam("id") final Long id, @Context final UriInfo uriInfo) {
+
+        this.context.authenticatedUser();
+
+        DeviceRegistrationData deviceRegistrationData = this.deviceRegistrationReadPlatformService.retrieveDeviceRegiistration(id);
+
+        return this.toApiJsonSerializer.serialize(deviceRegistrationData);
+    }
+
+    @PUT
+    @Path("{id}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String updateDeviceRegistration(@PathParam("id") final Long id, final String apiRequestBodyAsJson) {
+
+        this.context.authenticatedUser();
+
+        Gson gson = new Gson();
+        JsonObject json = new Gson().fromJson(apiRequestBodyAsJson, JsonObject.class);
+        Long clientId = json.get(DeviceRegistrationApiConstants.clientIdParamName).getAsLong();
+        String registrationId = json.get(DeviceRegistrationApiConstants.registrationIdParamName).getAsString();
+        DeviceRegistration deviceRegistration = this.deviceRegistrationWritePlatformService.updateDeviceRegistration(id, clientId,
+                registrationId);
+        String response = gson.toJson(deviceRegistration);
+        return response;
+    }
+
+    @DELETE
+    @Path("{id}")
+    @Consumes({ MediaType.APPLICATION_JSON })
+    @Produces({ MediaType.APPLICATION_JSON })
+    public String delete(@PathParam("id") final Long id) {
+        
+        this.context.authenticatedUser();
+        this.deviceRegistrationWritePlatformService.deleteDeviceRegistration(id);
+        return responseMap(id);
+        
+    }
+    
+    public String responseMap(Long id){
+        HashMap<String, Object> responseMap = new HashMap<>();
+        responseMap.put("resource", id);
+        return new Gson().toJson(responseMap);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistration.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistration.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistration.java
new file mode 100644
index 0000000..c4ba7b3
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistration.java
@@ -0,0 +1,85 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.gcm.domain;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToOne;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
+import org.apache.fineract.infrastructure.core.service.DateUtils;
+import org.apache.fineract.portfolio.client.domain.Client;
+
+@Entity
+@Table(name = "client_device_registration")
+public class DeviceRegistration extends AbstractPersistableCustom<Long> {
+
+	@OneToOne
+	@JoinColumn(name = "client_id", nullable = false, unique = true)
+	private Client client;
+
+	@Column(name = "registration_id", nullable = false, unique = true)
+	private String registrationId;
+
+	@Column(name = "updatedon_date", nullable = false)
+	@Temporal(TemporalType.TIMESTAMP)
+	private Date updatedOnDate;
+
+	private DeviceRegistration(final Client client, final String registrationId) {
+		this.client = client;
+		this.registrationId = registrationId;
+		this.updatedOnDate = DateUtils.getLocalDateTimeOfTenant().toDate();
+	}
+
+	public static DeviceRegistration instance(final Client client,
+			final String registrationId) {
+		return new DeviceRegistration(client, registrationId);
+	}
+
+	public Client getClient() {
+		return this.client;
+	}
+
+	public void setClient(Client client) {
+		this.client = client;
+	}
+
+	public String getRegistrationId() {
+		return this.registrationId;
+	}
+
+	public void setRegistrationId(String registrationId) {
+		this.registrationId = registrationId;
+	}
+
+	public Date getUpdatedOnDate() {
+		return this.updatedOnDate;
+	}
+
+	public void setUpdatedOnDate(Date updatedOnDate) {
+		this.updatedOnDate = updatedOnDate;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistrationData.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistrationData.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistrationData.java
new file mode 100644
index 0000000..3397a9b
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistrationData.java
@@ -0,0 +1,47 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.gcm.domain;
+
+import java.util.Date;
+
+import org.apache.fineract.portfolio.client.data.ClientData;
+
+public class DeviceRegistrationData {
+
+	public Long id;
+	public ClientData clientData;
+	public String registrationId;
+	public Date updatedOnDate;
+
+	private DeviceRegistrationData(final Long id, final ClientData clientData,
+			final String registrationId, final Date updatedOnDate) {
+		this.id = id;
+		this.clientData = clientData;
+		this.registrationId = registrationId;
+		this.updatedOnDate = updatedOnDate;
+	}
+
+	public static DeviceRegistrationData instance(final Long id,
+			final ClientData clientData, final String registrationId,
+			final Date updatedOnDate) {
+		return new DeviceRegistrationData(id, clientData, registrationId,
+				updatedOnDate);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistrationRepository.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistrationRepository.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistrationRepository.java
new file mode 100644
index 0000000..0033117
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistrationRepository.java
@@ -0,0 +1,36 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.gcm.domain;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+public interface DeviceRegistrationRepository extends
+		JpaRepository<DeviceRegistration, Long>,
+		JpaSpecificationExecutor<DeviceRegistration> {
+
+	public static final String FIND_DEVICE_REGISTRATION_BY_CLIENT = "select dr from DeviceRegistration dr where dr.client.id =:clientId ";
+
+	@Query(FIND_DEVICE_REGISTRATION_BY_CLIENT)
+	DeviceRegistration findDeviceRegistrationByClientId(
+			@Param("clientId") Long clientId);
+
+}

http://git-wip-us.apache.org/repos/asf/fineract/blob/8f30c210/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistrationRepositoryWrapper.java
----------------------------------------------------------------------
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistrationRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistrationRepositoryWrapper.java
new file mode 100644
index 0000000..a36b476
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/gcm/domain/DeviceRegistrationRepositoryWrapper.java
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.gcm.domain;
+
+import org.apache.fineract.infrastructure.gcm.exception.DeviceRegistrationNotFoundException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class DeviceRegistrationRepositoryWrapper {
+
+	private final DeviceRegistrationRepository repository;
+
+	@Autowired
+	public DeviceRegistrationRepositoryWrapper(
+			DeviceRegistrationRepository repository) {
+		this.repository = repository;
+	}
+
+	public DeviceRegistration findOneWithNotFoundDetection(
+			final Long deviceRegistrationId) {
+		final DeviceRegistration deviceRegistration = this.repository
+				.findOne(deviceRegistrationId);
+		if (deviceRegistration == null) {
+			throw new DeviceRegistrationNotFoundException(deviceRegistrationId);
+		}
+		return deviceRegistration;
+	}
+
+	public void save(final DeviceRegistration deviceRegistration) {
+		this.repository.save(deviceRegistration);
+	}
+
+	public void delete(final DeviceRegistration deviceRegistration) {
+		this.repository.delete(deviceRegistration);
+	}
+
+	public void saveAndFlush(final DeviceRegistration deviceRegistration) {
+		this.repository.saveAndFlush(deviceRegistration);
+	}
+
+	public DeviceRegistration findDeviceRegistrationByClientId(Long clientId) {
+		return this.repository.findDeviceRegistrationByClientId(clientId);
+	}
+}