← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 11408: fix test fail

 

------------------------------------------------------------
revno: 11408
committer: Long <Long@Long-Laptop>
branch nick: dhis2
timestamp: Tue 2013-07-16 18:33:35 +0700
message:
  fix test fail
added:
  dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/DhisMessageAlertParser.java
  dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/J2MEDataEntryParser.java
  dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/SMSParserKeyValue.java
modified:
  dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/sms/DataValueSMSListener.java
  dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml
  dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/DefaultParserManager.java
  dhis-2/dhis-services/dhis-service-sms/src/main/resources/META-INF/dhis/beans.xml


--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk

Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/sms/DataValueSMSListener.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/sms/DataValueSMSListener.java	2013-07-16 06:53:26 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/sms/DataValueSMSListener.java	2013-07-16 11:33:35 +0000
@@ -73,7 +73,7 @@
 
     private DataValueService dataValueService;
 
-    private OutboundSmsService outboundSmsService;
+//    private OutboundSmsService outboundSmsService;
 
     private DataElementCategoryService dataElementCategoryService;
 
@@ -494,14 +494,14 @@
         }
         notInReport = notInReport.substring( 0, notInReport.length() - 1 );
 
-        if ( codesWithoutDataValues.size() > 0 )
-        {
-            outboundSmsService.sendMessage( reportBack + notInReport, sender );
-        }
-        else
-        {
-            outboundSmsService.sendMessage( reportBack, sender );
-        }
+//        if ( codesWithoutDataValues.size() > 0 )
+//        {
+//            outboundSmsService.sendMessage( reportBack + notInReport, sender );
+//        }
+//        else
+//        {
+//            outboundSmsService.sendMessage( reportBack, sender );
+//        }
     }
 
     private void registerCompleteDataSet( DataSet dataSet, Period period, OrganisationUnit organisationUnit,
@@ -552,15 +552,15 @@
         this.dataValueService = dataValueService;
     }
 
-    public OutboundSmsService getOutboundSmsService()
-    {
-        return outboundSmsService;
-    }
-
-    public void setOutboundSmsService( OutboundSmsService outboundSmsService )
-    {
-        this.outboundSmsService = outboundSmsService;
-    }
+//    public OutboundSmsService getOutboundSmsService()
+//    {
+//        return outboundSmsService;
+//    }
+//
+//    public void setOutboundSmsService( OutboundSmsService outboundSmsService )
+//    {
+//        this.outboundSmsService = outboundSmsService;
+//    }
 
     public SMSCommandService getSmsCommandService()
     {

=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml	2013-07-16 06:53:26 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml	2013-07-16 11:33:35 +0000
@@ -970,41 +970,6 @@
     <property name="mappingService" ref="org.hisp.dhis.mapping.MappingService" />
     <property name="operandService" ref="org.hisp.dhis.dataelement.DataElementOperandService" />
   </bean>
-
-  <!-- SMS Listener -->
-  
-  <bean id="org.hisp.dhis.sms.DataValueSMSListener" class="org.hisp.dhis.sms.DataValueSMSListener">
-    <property name="registrationService" ref="org.hisp.dhis.dataset.CompleteDataSetRegistrationService" />
-    <property name="dataValueService" ref="org.hisp.dhis.datavalue.DataValueService" />
-    <property name="outboundSmsService" ref="org.hisp.dhis.sms.outbound.OutboundSmsService" />
-    <property name="userService" ref="org.hisp.dhis.user.UserService" />
-    <property name="smsCommandService" ref="smsCommandService" />
-    <property name="dataSetService" ref="org.hisp.dhis.dataset.DataSetService" />
-    <property name="dataElementCategoryService" ref="org.hisp.dhis.dataelement.DataElementCategoryService" />
-  </bean>
-  
-   <bean id="org.hisp.dhis.sms.UnregisteredSMSListener" class="org.hisp.dhis.sms.UnregisteredSMSListener">
-    <property name="messageConversationStore" ref="org.hisp.dhis.message.MessageConversationStore" />
-    <property name="userService" ref="org.hisp.dhis.user.UserService" />
-    <property name="smsCommandService" ref="smsCommandService" />
-  </bean>
-  
-   <bean id="org.hisp.dhis.sms.DHISMessageAlertListener" class="org.hisp.dhis.sms.DHISMessageAlertListener">
-    <property name="smsMessageSender" ref="org.hisp.dhis.sms.outbound.SmsSender" />
-    <property name="emailMessageSender" ref="emailMessageSender" />
-    <property name="messageConversationStore" ref="org.hisp.dhis.message.MessageConversationStore" />
-    <property name="userService" ref="org.hisp.dhis.user.UserService" />
-    <property name="smsCommandService" ref="smsCommandService" />
-  </bean>
-  
-   <bean id="org.hisp.dhis.sms.J2MEDataValueSMSListener" class="org.hisp.dhis.sms.J2MEDataValueSMSListener">
-    <property name="dataValueService" ref="org.hisp.dhis.datavalue.DataValueService" />
-    <property name="dataElementCategoryService" ref="org.hisp.dhis.dataelement.DataElementCategoryService" />
-    <property name="smsCommandService" ref="smsCommandService" />
-    <property name="userService" ref="org.hisp.dhis.user.UserService" />
-    <property name="registrationService" ref="org.hisp.dhis.dataset.CompleteDataSetRegistrationService" />
-    <property name="outboundSmsService" ref="org.hisp.dhis.sms.outbound.OutboundSmsService" />
-  </bean>
   
   <!-- DeletionManager -->
 

=== modified file 'dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/DefaultParserManager.java'
--- dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/DefaultParserManager.java	2013-07-16 06:53:26 +0000
+++ dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/DefaultParserManager.java	2013-07-16 11:33:35 +0000
@@ -27,18 +27,61 @@
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.hisp.dhis.sms.DHISMessageAlertListener;
-import org.hisp.dhis.sms.DataValueSMSListener;
-import org.hisp.dhis.sms.J2MEDataValueSMSListener;
-import org.hisp.dhis.sms.UnregisteredSMSListener;
+import org.hisp.dhis.dataelement.DataElement;
+import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo;
+import org.hisp.dhis.dataelement.DataElementCategoryService;
+import org.hisp.dhis.dataset.CompleteDataSetRegistration;
+import org.hisp.dhis.dataset.CompleteDataSetRegistrationService;
+import org.hisp.dhis.dataset.DataSet;
+import org.hisp.dhis.dataset.DataSetService;
+import org.hisp.dhis.datavalue.DataValue;
+import org.hisp.dhis.datavalue.DataValueService;
+import org.hisp.dhis.message.Message;
+import org.hisp.dhis.message.MessageConversation;
+import org.hisp.dhis.message.MessageConversationStore;
+import org.hisp.dhis.message.MessageSender;
+import org.hisp.dhis.message.UserMessage;
+import org.hisp.dhis.organisationunit.OrganisationUnit;
+import org.hisp.dhis.patient.Patient;
+import org.hisp.dhis.period.CalendarPeriodType;
+import org.hisp.dhis.period.DailyPeriodType;
+import org.hisp.dhis.period.MonthlyPeriodType;
+import org.hisp.dhis.period.Period;
+import org.hisp.dhis.period.PeriodType;
+import org.hisp.dhis.period.QuarterlyPeriodType;
+import org.hisp.dhis.period.WeeklyPeriodType;
+import org.hisp.dhis.period.YearlyPeriodType;
 import org.hisp.dhis.sms.incoming.IncomingSms;
 import org.hisp.dhis.sms.incoming.IncomingSmsService;
 import org.hisp.dhis.sms.incoming.SmsMessageStatus;
 import org.hisp.dhis.sms.outbound.OutboundSms;
 import org.hisp.dhis.sms.outbound.OutboundSmsService;
 import org.hisp.dhis.sms.outbound.OutboundSmsTransportService;
+import org.hisp.dhis.smscommand.SMSCode;
+import org.hisp.dhis.smscommand.SMSCommand;
+import org.hisp.dhis.smscommand.SMSCommandService;
+import org.hisp.dhis.system.util.ValidationUtils;
+import org.hisp.dhis.user.User;
+import org.hisp.dhis.user.UserCredentials;
+import org.hisp.dhis.user.UserGroup;
+import org.hisp.dhis.user.UserService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Required;
 import org.springframework.transaction.annotation.Transactional;
@@ -53,20 +96,47 @@
 
     private static final Log log = LogFactory.getLog( DefaultParserManager.class );
 
-    private J2MEDataValueSMSListener j2meDataValueSMSListener;
-
-    private DataValueSMSListener dataValueSMSListener;
-
-    private UnregisteredSMSListener unregisteredSMSListener;
-
-    private DHISMessageAlertListener dhisMessageAlertListener;
+    private CompleteDataSetRegistrationService registrationService;
+
+    private DataValueService dataValueService;
+
+    private UserService userService;
+
+    private SMSCommandService smsCommandService;
 
     private OutboundSmsService outboundSmsService;
 
     @Autowired
+    private DataElementCategoryService dataElementCategoryService;
+
+    @Autowired
     private OutboundSmsTransportService transportService;
 
     @Autowired
+    private DataSetService dataSetService;
+
+    private MessageConversationStore messageConversationStore;
+
+    public void setMessageConversationStore( MessageConversationStore messageConversationStore )
+    {
+        this.messageConversationStore = messageConversationStore;
+    }
+
+    private MessageSender smsMessageSender;
+
+    public void setSmsMessageSender( MessageSender smsMessageSender )
+    {
+        this.smsMessageSender = smsMessageSender;
+    }
+
+    private MessageSender emailMessageSender;
+    
+    public void setEmailMessageSender( MessageSender emailMessageSender )
+    {
+        this.emailMessageSender = emailMessageSender;
+    }
+
+    @Autowired
     private IncomingSmsService incomingSmsService;
 
     @Transactional
@@ -75,30 +145,13 @@
     {
         try
         {
-            if ( dataValueSMSListener.accept( sms ) )
-            {
-                dataValueSMSListener.receive( sms );
-            }
-            else if ( j2meDataValueSMSListener.accept( sms ) )
-            {
-                j2meDataValueSMSListener.receive( sms );
-            }
-            else if ( dhisMessageAlertListener.accept( sms ) )
-            {
-                dhisMessageAlertListener.receive( sms );
-            }
-            else if ( unregisteredSMSListener.accept( sms ) )
-            {
-                unregisteredSMSListener.receive( sms );
-            }
-            else
-            {
-                throw new SMSParserException( "Command does not exist" );
-            }
+            parse( sms.getOriginator(), sms );
+            sms.setStatus( SmsMessageStatus.PROCESSED );
+            incomingSmsService.update( sms );
         }
         catch ( SMSParserException e )
         {
-            log.error( e.getMessage() );
+            e.printStackTrace();
             sms.setStatus( SmsMessageStatus.FAILED );
             incomingSmsService.update( sms );
             sendSMS( e.getMessage(), sms.getOriginator() );
@@ -115,60 +168,899 @@
         }
     }
 
+    @Transactional
+    private void parse( String sender, IncomingSms sms )
+        throws SMSParserException
+    {
+        String message = sms.getText();
+        if ( StringUtils.isEmpty( sender ) )
+        {
+            return;
+        }
+
+        sender = StringUtils.replace( sender, "+", "" );
+
+        if ( StringUtils.isEmpty( message ) )
+        {
+            throw new SMSParserException( "No command in SMS" );
+        }
+
+        String commandString = null;
+        // here, check command first
+        if ( message.indexOf( " " ) > 0 )
+        {
+            commandString = message.substring( 0, message.indexOf( " " ) );
+            message = message.substring( commandString.length() );
+        }
+        else
+        {
+            commandString = message;
+        }
+
+        boolean foundCommand = false;
+
+        Collection<OrganisationUnit> orgUnits = getOrganisationUnitsByPhoneNumber( sender );
+
+        for ( SMSCommand command : smsCommandService.getSMSCommands() )
+        {
+            if ( command.getName().equalsIgnoreCase( commandString ) )
+            {
+                foundCommand = true;
+                if ( ParserType.KEY_VALUE_PARSER.equals( command.getParserType() ) )
+                {
+                    checkIfDHISUsers( orgUnits, sender );
+                    runKeyValueParser( sender, message, orgUnits, command );
+                    break;
+                }
+                else if ( ParserType.J2ME_PARSER.equals( command.getParserType() ) )
+                {
+                    checkIfDHISUsers( orgUnits, sender );
+                    runJ2MEParser( sender, message, orgUnits, command );
+                    break;
+                }
+                else if ( ParserType.ALERT_PARSER.equals( command.getParserType() ) )
+                {
+                    checkIfDHISUsers( orgUnits, sender );
+                    runDhisMessageAlertParser( sender, message, command );
+                    break;
+                }
+                else if ( ParserType.UNREGISTERED_PARSER.equals( command.getParserType() ) )
+                {
+                    runUnregisteredParser( sender, message, command );
+                    break;
+                }
+            }
+        }
+        if ( !foundCommand )
+        {
+            Collection<Patient> patientList = new ArrayList<Patient>(); //TODO FIX! //patientService.getPatientsByPhone( sender, null, null );
+            
+            if ( !patientList.isEmpty() )
+            {
+                for ( Patient each : patientList )
+                {
+                    if ( each.getHealthWorker() != null )
+                    {
+                        UserCredentials patientUser = userService.getUserCredentialsByUsername( "system" );
+
+                        MessageConversation conversation = new MessageConversation( "Patients' Message", patientUser.getUser() );
+
+                        conversation.addMessage( new Message( sms.getText().trim(), null, patientUser.getUser() ) );
+
+                        conversation.addUserMessage( new UserMessage( each.getHealthWorker(), false ) );
+                        
+                        messageConversationStore.save( conversation );
+                    }
+                }
+            }
+            else
+            {
+                throw new SMSParserException( "Command '" + commandString + "' does not exist" );
+            }
+        }
+    }
+
+    protected Collection<OrganisationUnit> getOrganisationUnitsByPhoneNumber( String sender )
+    {
+        Collection<OrganisationUnit> orgUnits = new ArrayList<OrganisationUnit>();
+        Collection<User> users = userService.getUsersByPhoneNumber( sender );
+        for ( User u : users )
+        {
+            if ( u.getOrganisationUnits() != null )
+            {
+                orgUnits.addAll( u.getOrganisationUnits() );
+            }
+        }
+
+        return orgUnits;
+    }
+
+    private void checkIfDHISUsers( Collection<OrganisationUnit> orgUnits, String sender )
+    {
+        if ( orgUnits == null || orgUnits.size() == 0 )
+        {
+            log.info( "No user found for phone number: " + sender );
+            throw new SMSParserException( "No user associated with this phone number. Please contact your supervisor." );
+        }
+    }
+
+    private void runKeyValueParser( String sender, String message, Collection<OrganisationUnit> orgUnits,
+        SMSCommand command )
+    {
+        IParser p = new SMSParserKeyValue();
+        if ( !StringUtils.isBlank( command.getSeparator() ) )
+        {
+            p.setSeparator( command.getSeparator() );
+        }
+
+        Map<String, String> parsedMessage = p.parse( message );
+        Date date = lookForDate( message );
+        OrganisationUnit orgUnit = selectOrganisationUnit( orgUnits, parsedMessage );
+        Period period = getPeriod( command, date );
+
+        // Check if Data Set is locked
+        if ( dataSetService.isLocked( command.getDataset(), period, orgUnit, null ) )
+        {
+            throw new SMSParserException( "Dataset is locked for the period " + period.getStartDate() + " - "
+                + period.getEndDate() );
+        }
+
+        boolean valueStored = false;
+        for ( SMSCode code : command.getCodes() )
+        {
+            if ( parsedMessage.containsKey( code.getCode().toUpperCase() ) )
+            {
+                valueStored = storeDataValue( sender, orgUnit, parsedMessage, code, command, date,
+                    command.getDataset(), formIsComplete( command, parsedMessage ) );
+            }
+        }
+
+        if ( parsedMessage.isEmpty() )
+        {
+            if ( StringUtils.isEmpty( command.getDefaultMessage() ) )
+            {
+                throw new SMSParserException( "No values reported for command '" + command.getName() + "'" );
+            }
+            else
+            {
+                throw new SMSParserException( command.getDefaultMessage() );
+            }
+        }
+        else if ( !valueStored )
+        {
+            throw new SMSParserException( "Wrong format for command '" + command.getName() + "'" );
+        }
+
+        markCompleteDataSet( sender, orgUnit, parsedMessage, command, date );
+        sendSuccessFeedback( sender, command, parsedMessage, date, orgUnit );
+    }
+
+    private void runDhisMessageAlertParser( String senderNumber, String message, SMSCommand command )
+    {
+        UserGroup userGroup = command.getUserGroup();
+
+        if ( userGroup != null )
+        {
+            Collection<User> users = userService.getUsersByPhoneNumber( senderNumber );
+            
+            if ( users != null && users.size() > 1 )
+            {
+                String messageMoreThanOneUser = "System only accepts sender's number assigned for one user, but found more than one user for this number: ";
+                for ( Iterator<User> i = users.iterator(); i.hasNext(); )
+                {
+                    User user = i.next();
+                    messageMoreThanOneUser += " " + user.getName();
+                    if ( i.hasNext() )
+                    {
+                        messageMoreThanOneUser += ",";
+                    }
+                }
+                throw new SMSParserException( messageMoreThanOneUser );
+            }
+            else if ( users != null && users.size() == 1 )
+            {
+                User sender = users.iterator().next();
+
+                Set<User> receivers = new HashSet<User>( userGroup.getMembers() );
+                
+                // forward to user group by SMS
+                smsMessageSender.sendMessage( command.getName(), message, sender, receivers, true );
+                
+                // forward to user group by E-mail
+                emailMessageSender.sendMessage( command.getName(), message, sender, receivers, false );
+
+                // forward to user group by dhis message
+                if ( sender != null )
+                {
+                    receivers.add( sender );
+                }
+
+                MessageConversation conversation = new MessageConversation( command.getName(), sender );
+
+                conversation.addMessage( new Message( message, null, sender ) );
+
+                for ( User receiver : receivers )
+                {
+                    boolean read = receiver != null && receiver.equals( sender );
+
+                    conversation.addUserMessage( new UserMessage( receiver, read ) );
+                }
+                messageConversationStore.save( conversation );
+                // confirm SMS was received and forwarded completely
+                Set<User> feedbackList = new HashSet<User>();
+                feedbackList.add( sender );
+                smsMessageSender.sendMessage( command.getName(), command.getReceivedMessage(), null, feedbackList, true );                
+            }
+        }
+    }
+
+    private void runUnregisteredParser( String senderNumber, String message, SMSCommand command )
+    {
+        UserGroup userGroup = command.getUserGroup();
+
+        if ( userGroup != null )
+        {
+            Set<User> receivers = new HashSet<User>( userGroup.getMembers() );
+
+            UserCredentials anonymousUser = userService.getUserCredentialsByUsername( "system" );
+
+            MessageConversation conversation = new MessageConversation( command.getName(), anonymousUser.getUser() );
+
+            conversation.addMessage( new Message( message, null, anonymousUser.getUser() ) );
+
+            for ( User receiver : receivers )
+            {
+                boolean read = false;
+
+                conversation.addUserMessage( new UserMessage( receiver, read ) );
+            }
+            
+            messageConversationStore.save( conversation );
+        }
+    }
+
+    protected OrganisationUnit selectOrganisationUnit( Collection<OrganisationUnit> orgUnits,
+        Map<String, String> parsedMessage )
+    {
+        OrganisationUnit orgUnit = null;
+
+        for ( OrganisationUnit o : orgUnits )
+        {
+            if ( orgUnits.size() == 1 )
+            {
+                orgUnit = o;
+            }
+            if ( parsedMessage.containsKey( "ORG" ) && o.getCode().equals( parsedMessage.get( "ORG" ) ) )
+            {
+                orgUnit = o;
+                break;
+            }
+        }
+
+        if ( orgUnit == null && orgUnits.size() > 1 )
+        {
+            String messageListingOrgUnits = "Found more than one org unit for this number. Please specify one of the following:";
+            for ( Iterator<OrganisationUnit> i = orgUnits.iterator(); i.hasNext(); )
+            {
+                OrganisationUnit o = i.next();
+                messageListingOrgUnits += " " + o.getName() + ":" + o.getCode();
+                if ( i.hasNext() )
+                {
+                    messageListingOrgUnits += ",";
+                }
+            }
+            throw new SMSParserException( messageListingOrgUnits );
+        }
+        return orgUnit;
+    }
+
+    protected void sendSuccessFeedback( String sender, SMSCommand command, Map<String, String> parsedMessage,
+        Date date, OrganisationUnit orgunit )
+    {
+        String reportBack = "Thank you! Values entered: ";
+        String notInReport = "Missing values for: ";
+
+        Period period = null;
+
+        Map<String, DataValue> codesWithDataValues = new TreeMap<String, DataValue>();
+        List<String> codesWithoutDataValues = new ArrayList<String>();
+
+        for ( SMSCode code : command.getCodes() )
+        {
+
+            DataElementCategoryOptionCombo optionCombo = dataElementCategoryService
+                .getDataElementCategoryOptionCombo( code.getOptionId() );
+
+            period = getPeriod( command, date );
+
+            DataValue dv = dataValueService.getDataValue( orgunit, code.getDataElement(), period, optionCombo );
+
+            if ( dv == null && !StringUtils.isEmpty( code.getCode() ) )
+            {
+                codesWithoutDataValues.add( code.getCode() );
+            }
+            else if ( dv != null )
+            {
+                codesWithDataValues.put( code.getCode(), dv );
+            }
+        }
+
+        for ( String key : codesWithDataValues.keySet() )
+        {
+            DataValue dv = codesWithDataValues.get( key );
+            String value = dv.getValue();
+            if ( StringUtils.equals( dv.getDataElement().getType(), DataElement.VALUE_TYPE_BOOL ) )
+            {
+                if ( "true".equals( value ) )
+                {
+                    value = "Yes";
+                }
+                else if ( "false".equals( value ) )
+                {
+                    value = "No";
+                }
+            }
+            reportBack += key + "=" + value + " ";
+        }
+
+        Collections.sort( codesWithoutDataValues );
+
+        for ( String key : codesWithoutDataValues )
+        {
+            notInReport += key + ",";
+        }
+        notInReport = notInReport.substring( 0, notInReport.length() - 1 );
+
+        if ( codesWithoutDataValues.size() > 0 )
+        {
+            sendSMS( reportBack + notInReport, sender );
+        }
+        else
+        {
+            sendSMS( reportBack, sender );
+        }
+    }
+
+    protected Period getPeriod( SMSCommand command, Date date )
+    {
+
+        Period period;
+        period = command.getDataset().getPeriodType().createPeriod();
+        CalendarPeriodType cpt = (CalendarPeriodType) period.getPeriodType();
+        if ( command.isCurrentPeriodUsedForReporting() )
+        {
+            period = cpt.createPeriod( new Date() );
+        }
+        else
+        {
+            period = cpt.getPreviousPeriod( period );
+        }
+
+        if ( date != null )
+        {
+            period = cpt.createPeriod( date );
+        }
+
+        return period;
+    }
+
+    private Date lookForDate( String message )
+    {
+        if ( !message.contains( " " ) )
+        {
+            return null;
+        }
+        
+        Date date = null;
+        String dateString = message.trim().split( " " )[0];
+        SimpleDateFormat format = new SimpleDateFormat( "ddMM" );
+
+        try
+        {
+            Calendar cal = Calendar.getInstance();
+            date = format.parse( dateString );
+            cal.setTime( date );
+            int year = Calendar.getInstance().get( Calendar.YEAR );
+            int month = Calendar.getInstance().get( Calendar.MONTH );
+            if ( cal.get( Calendar.MONTH ) < month )
+            {
+                cal.set( Calendar.YEAR, year );
+            }
+            else
+            {
+                cal.set( Calendar.YEAR, year - 1 );
+            }
+            date = cal.getTime();
+        }
+        catch ( Exception e )
+        {
+            // no date found
+        }
+        return date;
+    }
+
+    private boolean storeDataValue( String sender, OrganisationUnit orgunit, Map<String, String> parsedMessage,
+        SMSCode code, SMSCommand command, Date date, DataSet dataSet, boolean completeForm )
+    {
+        String upperCaseCode = code.getCode().toUpperCase();
+
+        String storedBy = getUser( sender ).getUsername();
+
+        if ( StringUtils.isBlank( storedBy ) )
+        {
+            storedBy = "[unknown] from [" + sender + "]";
+        }
+
+        DataElementCategoryOptionCombo optionCombo = dataElementCategoryService.getDataElementCategoryOptionCombo( code
+            .getOptionId() );
+
+        Period period = getPeriod( command, date );
+
+        DataValue dv = dataValueService.getDataValue( orgunit, code.getDataElement(), period, optionCombo );
+
+        String value = parsedMessage.get( upperCaseCode );
+        if ( !StringUtils.isEmpty( value ) )
+        {
+            boolean newDataValue = false;
+            if ( dv == null )
+            {
+                dv = new DataValue();
+                dv.setOptionCombo( optionCombo );
+                dv.setSource( orgunit );
+                dv.setDataElement( code.getDataElement() );
+                dv.setPeriod( period );
+                dv.setComment( "" );
+                newDataValue = true;
+            }
+
+            if ( StringUtils.equals( dv.getDataElement().getType(), DataElement.VALUE_TYPE_BOOL ) )
+            {
+                if ( "Y".equals( value.toUpperCase() ) || "YES".equals( value.toUpperCase() ) )
+                {
+                    value = "true";
+                }
+                else if ( "N".equals( value.toUpperCase() ) || "NO".equals( value.toUpperCase() ) )
+                {
+                    value = "false";
+                }
+            }
+            else if ( StringUtils.equals( dv.getDataElement().getType(), DataElement.VALUE_TYPE_INT ) )
+            {
+                try
+                {
+                    Integer.parseInt( value );
+                }
+                catch ( NumberFormatException e )
+                {
+                    return false;
+                }
+
+            }
+
+            dv.setValue( value );
+            dv.setTimestamp( new java.util.Date() );
+            dv.setStoredBy( storedBy );
+
+            if ( newDataValue )
+            {
+                dataValueService.addDataValue( dv );
+            }
+            else
+            {
+                dataValueService.updateDataValue( dv );
+            }
+        }
+
+        return true;
+    }
+
+    /* Checks if all defined data codes have values in the database */
+    private void markCompleteDataSet( String sender, OrganisationUnit orgunit, Map<String, String> parsedMessage,
+        SMSCommand command, Date date )
+    {
+
+        Period period = null;
+
+        for ( SMSCode code : command.getCodes() )
+        {
+
+            DataElementCategoryOptionCombo optionCombo = dataElementCategoryService
+                .getDataElementCategoryOptionCombo( code.getOptionId() );
+
+            period = getPeriod( command, date );
+
+            DataValue dv = dataValueService.getDataValue( orgunit, code.getDataElement(), period, optionCombo );
+
+            if ( dv == null && !StringUtils.isEmpty( code.getCode() ) )
+            {
+                return; // not marked as complete
+            }
+        }
+
+        String storedBy = getUser( sender ).getUsername();
+
+        if ( StringUtils.isBlank( storedBy ) )
+        {
+            storedBy = "[unknown] from [" + sender + "]";
+        }
+
+        // if new values are submitted re-register as complete
+        deregisterCompleteDataSet( command.getDataset(), period, orgunit );
+        registerCompleteDataSet( command.getDataset(), period, orgunit, storedBy );
+
+    }
+
+    private boolean formIsComplete( SMSCommand command, Map<String, String> parsedMessage )
+    {
+        for ( SMSCode code : command.getCodes() )
+        {
+            if ( !parsedMessage.containsKey( code.getCode().toUpperCase() ) )
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void registerCompleteDataSet( DataSet dataSet, Period period, OrganisationUnit organisationUnit,
+        String storedBy )
+    {
+        CompleteDataSetRegistration registration = new CompleteDataSetRegistration();
+
+        if ( registrationService.getCompleteDataSetRegistration( dataSet, period, organisationUnit ) == null )
+        {
+            registration.setDataSet( dataSet );
+            registration.setPeriod( period );
+            registration.setSource( organisationUnit );
+            registration.setDate( new Date() );
+            registration.setStoredBy( storedBy );
+            registration.setPeriodName( registration.getPeriod().toString() );
+            registrationService.saveCompleteDataSetRegistration( registration, false );
+            log.info( "DataSet registered as complete: " + registration );
+        }
+    }
+
+    private void deregisterCompleteDataSet( DataSet dataSet, Period period, OrganisationUnit organisationUnit )
+    {
+        CompleteDataSetRegistration registration = registrationService.getCompleteDataSetRegistration( dataSet, period,
+            organisationUnit );
+
+        if ( registration != null )
+        {
+            registrationService.deleteCompleteDataSetRegistration( registration );
+
+            log.info( "DataSet un-registered as complete: " + registration );
+        }
+    }
+
+    private User getUser( String sender )
+    {
+        OrganisationUnit orgunit = null;
+        User user = null;
+        for ( User u : userService.getUsersByPhoneNumber( sender ) )
+        {
+            OrganisationUnit ou = u.getOrganisationUnit();
+
+            // Might be undefined if the user has more than one org.units
+            // "attached"
+            if ( orgunit == null )
+            {
+                orgunit = ou;
+            }
+            else if ( orgunit.getId() == ou.getId() )
+            {
+                // same orgunit, no problem...
+            }
+            else
+            {
+                throw new SMSParserException(
+                    "User is associated with more than one orgunit. Please contact your supervisor." );
+            }
+            user = u;
+        }
+        return user;
+    }
+
+    // Run the J2ME parser for mobile
+    private void runJ2MEParser( String sender, String message, Collection<OrganisationUnit> orgUnits, SMSCommand command )
+    {
+        J2MEDataEntryParser j2meParser = new J2MEDataEntryParser();
+        j2meParser.setSmsCommand( command );
+        message = message.trim();
+
+        if ( !StringUtils.isBlank( command.getSeparator() ) )
+        {
+            j2meParser.setSeparator( command.getSeparator() );
+        }
+        String token[] = message.split( "!" );
+        Period period = getPeriod( token[0].trim(), command.getDataset().getPeriodType() );
+        Map<String, String> parsedMessage = j2meParser.parse( token[1] );
+        OrganisationUnit orgUnit = selectOrganisationUnit( orgUnits, parsedMessage );
+
+        boolean valueStored = false;
+        for ( SMSCode code : command.getCodes() )
+        {
+            if ( parsedMessage.containsKey( code.getCode().toUpperCase() ) )
+            {
+                storeDataValue( sender, orgUnit, parsedMessage, code, command, period, command.getDataset(),
+                    formIsComplete( command, parsedMessage ) );
+                valueStored = true;
+            }
+        }
+
+        if ( parsedMessage.isEmpty() || !valueStored )
+        {
+            if ( StringUtils.isEmpty( command.getDefaultMessage() ) )
+            {
+                throw new SMSParserException( "No values reported for command '" + command.getName() + "'" );
+            }
+            else
+            {
+                throw new SMSParserException( command.getDefaultMessage() );
+            }
+        }
+
+        registerCompleteDataSet( command.getDataset(), period, orgUnit, "mobile" );
+
+        sendSuccessFeedback( sender, command, parsedMessage, period, orgUnit );
+    }
+
+    private void sendSuccessFeedback( String sender, SMSCommand command, Map<String, String> parsedMessage,
+        Period period, OrganisationUnit orgUnit )
+    {
+        String reportBack = "Thank you! Values entered: ";
+        String notInReport = "Missing values for: ";
+        boolean missingElements = false;
+
+        for ( SMSCode code : command.getCodes() )
+        {
+
+            DataElementCategoryOptionCombo optionCombo = dataElementCategoryService
+                .getDataElementCategoryOptionCombo( code.getOptionId() );
+
+            DataValue dv = dataValueService.getDataValue( orgUnit, code.getDataElement(), period, optionCombo );
+
+            if ( dv == null && !StringUtils.isEmpty( code.getCode() ) )
+            {
+                notInReport += code.getCode() + ",";
+                missingElements = true;
+            }
+            else if ( dv != null )
+            {
+                String value = dv.getValue();
+                if ( StringUtils.equals( dv.getDataElement().getType(), DataElement.VALUE_TYPE_BOOL ) )
+                {
+                    if ( "true".equals( value ) )
+                    {
+                        value = "Yes";
+                    }
+                    else if ( "false".equals( value ) )
+                    {
+                        value = "No";
+                    }
+                }
+                reportBack += code.getCode() + "=" + value + " ";
+            }
+        }
+
+        notInReport = notInReport.substring( 0, notInReport.length() - 1 );
+
+        if ( missingElements )
+        {
+            sendSMS( reportBack + notInReport, sender );
+        }
+        else
+        {
+            sendSMS( reportBack, sender );
+        }
+
+    }
+
+    private void storeDataValue( String sender, OrganisationUnit orgUnit, Map<String, String> parsedMessage,
+        SMSCode code, SMSCommand command, Period period, DataSet dataset, boolean formIsComplete )
+    {
+        String upperCaseCode = code.getCode().toUpperCase();
+
+        String storedBy = getUser( sender ).getUsername();
+
+        if ( StringUtils.isBlank( storedBy ) )
+        {
+            storedBy = "[unknown] from [" + sender + "]";
+        }
+
+        DataElementCategoryOptionCombo optionCombo = dataElementCategoryService.getDataElementCategoryOptionCombo( code
+            .getOptionId() );
+
+        DataValue dv = dataValueService.getDataValue( orgUnit, code.getDataElement(), period, optionCombo );
+
+        String value = parsedMessage.get( upperCaseCode );
+        if ( !StringUtils.isEmpty( value ) )
+        {
+            boolean newDataValue = false;
+            if ( dv == null )
+            {
+                dv = new DataValue();
+                dv.setOptionCombo( optionCombo );
+                dv.setSource( orgUnit );
+                dv.setDataElement( code.getDataElement() );
+                dv.setPeriod( period );
+                dv.setComment( "" );
+                newDataValue = true;
+            }
+
+            if ( StringUtils.equals( dv.getDataElement().getType(), DataElement.VALUE_TYPE_BOOL ) )
+            {
+                if ( "Y".equals( value.toUpperCase() ) || "YES".equals( value.toUpperCase() ) )
+                {
+                    value = "true";
+                }
+                else if ( "N".equals( value.toUpperCase() ) || "NO".equals( value.toUpperCase() ) )
+                {
+                    value = "false";
+                }
+            }
+
+            dv.setValue( value );
+            dv.setTimestamp( new java.util.Date() );
+            dv.setStoredBy( storedBy );
+
+            if ( ValidationUtils.dataValueIsValid( value, dv.getDataElement() ) != null )
+            {
+                return; // not a valid value for data element
+            }
+
+            if ( newDataValue )
+            {
+                dataValueService.addDataValue( dv );
+            }
+            else
+            {
+                dataValueService.updateDataValue( dv );
+            }
+        }
+
+    }
+
+    public static Period getPeriod( String periodName, PeriodType periodType )
+        throws IllegalArgumentException
+    {
+
+        if ( periodType instanceof DailyPeriodType )
+        {
+            String pattern = "yyyy-MM-dd";
+            SimpleDateFormat formatter = new SimpleDateFormat( pattern );
+            Date date;
+            try
+            {
+                date = formatter.parse( periodName );
+            }
+            catch ( ParseException e )
+            {
+                throw new IllegalArgumentException( "Couldn't make a period of type " + periodType.getName()
+                    + " and name " + periodName, e );
+            }
+            return periodType.createPeriod( date );
+
+        }
+
+        if ( periodType instanceof WeeklyPeriodType )
+        {
+            String pattern = "yyyy-MM-dd";
+            SimpleDateFormat formatter = new SimpleDateFormat( pattern );
+            Date date;
+            try
+            {
+                date = formatter.parse( periodName );
+            }
+            catch ( ParseException e )
+            {
+                throw new IllegalArgumentException( "Couldn't make a period of type " + periodType.getName()
+                    + " and name " + periodName, e );
+            }
+            return periodType.createPeriod( date );
+        }
+
+        if ( periodType instanceof MonthlyPeriodType )
+        {
+            int dashIndex = periodName.indexOf( '-' );
+
+            if ( dashIndex < 0 )
+            {
+                return null;
+            }
+
+            int month = Integer.parseInt( periodName.substring( 0, dashIndex ) );
+            int year = Integer.parseInt( periodName.substring( dashIndex + 1, periodName.length() ) );
+
+            Calendar cal = Calendar.getInstance();
+            cal.set( Calendar.YEAR, year );
+            cal.set( Calendar.MONTH, month );
+
+            return periodType.createPeriod( cal.getTime() );
+        }
+
+        if ( periodType instanceof YearlyPeriodType )
+        {
+            Calendar cal = Calendar.getInstance();
+            cal.set( Calendar.YEAR, Integer.parseInt( periodName ) );
+
+            return periodType.createPeriod( cal.getTime() );
+        }
+
+        if ( periodType instanceof QuarterlyPeriodType )
+        {
+            Calendar cal = Calendar.getInstance();
+
+            int month = 0;
+            if ( periodName.substring( 0, periodName.indexOf( " " ) ).equals( "Jan" ) )
+            {
+                month = 1;
+            }
+            else if ( periodName.substring( 0, periodName.indexOf( " " ) ).equals( "Apr" ) )
+            {
+                month = 4;
+            }
+            else if ( periodName.substring( 0, periodName.indexOf( " " ) ).equals( "Jul" ) )
+            {
+                month = 6;
+            }
+            else if ( periodName.substring( 0, periodName.indexOf( " " ) ).equals( "Oct" ) )
+            {
+                month = 10;
+            }
+
+            int year = Integer.parseInt( periodName.substring( periodName.lastIndexOf( " " ) + 1 ) );
+
+            cal.set( Calendar.MONTH, month );
+            cal.set( Calendar.YEAR, year );
+
+            if ( month != 0 )
+            {
+                return periodType.createPeriod( cal.getTime() );
+            }
+
+        }
+
+        throw new IllegalArgumentException( "Couldn't make a period of type " + periodType.getName() + " and name "
+            + periodName );
+    }
+
+    public void setDataElementCategoryService( DataElementCategoryService dataElementCategoryService )
+    {
+        this.dataElementCategoryService = dataElementCategoryService;
+    }
+
+    @Required
+    public void setSmsCommandService( SMSCommandService smsCommandService )
+    {
+        this.smsCommandService = smsCommandService;
+    }
+
+    @Required
+    public void setDataValueService( DataValueService dataValueService )
+    {
+        this.dataValueService = dataValueService;
+    }
+
+    @Required
+    public void setUserService( UserService userService )
+    {
+        this.userService = userService;
+    }
+
+    public void setOutboundSmsService( OutboundSmsService outboundSmsService )
+    {
+        this.outboundSmsService = outboundSmsService;
+    }
+
+    public void setRegistrationService( CompleteDataSetRegistrationService registrationService )
+    {
+        this.registrationService = registrationService;
+    }
+
     @Required
     public IncomingSmsService getIncomingSmsService()
     {
         return incomingSmsService;
     }
-
-    public J2MEDataValueSMSListener getJ2meDataValueSMSListener()
-    {
-        return j2meDataValueSMSListener;
-    }
-
-    public void setJ2meDataValueSMSListener( J2MEDataValueSMSListener j2meDataValueSMSListener )
-    {
-        this.j2meDataValueSMSListener = j2meDataValueSMSListener;
-    }
-
-    public DataValueSMSListener getDataValueSMSListener()
-    {
-        return dataValueSMSListener;
-    }
-
-    public void setDataValueSMSListener( DataValueSMSListener dataValueSMSListener )
-    {
-        this.dataValueSMSListener = dataValueSMSListener;
-    }
-
-    public UnregisteredSMSListener getUnregisteredSMSListener()
-    {
-        return unregisteredSMSListener;
-    }
-
-    public void setUnregisteredSMSListener( UnregisteredSMSListener unregisteredSMSListener )
-    {
-        this.unregisteredSMSListener = unregisteredSMSListener;
-    }
-
-    public DHISMessageAlertListener getDhisMessageAlertListener()
-    {
-        return dhisMessageAlertListener;
-    }
-
-    public void setDhisMessageAlertListener( DHISMessageAlertListener dhisMessageAlertListener )
-    {
-        this.dhisMessageAlertListener = dhisMessageAlertListener;
-    }
-
-    public OutboundSmsService getOutboundSmsService()
-    {
-        return outboundSmsService;
-    }
-
-    public void setOutboundSmsService( OutboundSmsService outboundSmsService )
-    {
-        this.outboundSmsService = outboundSmsService;
-    }
-
 }

=== added file 'dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/DhisMessageAlertParser.java'
--- dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/DhisMessageAlertParser.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/DhisMessageAlertParser.java	2013-07-16 11:33:35 +0000
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2004-2012, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.hisp.dhis.sms.parse;
+
+import java.util.HashMap;
+import java.util.Map;
+
+ /**
+ * @author Nguyen Kim Lai
+ */
+public class DhisMessageAlertParser 
+    implements IParser
+{    
+    @Override
+    public Map<String, String> parse( String sms )
+    {
+        HashMap<String, String> output = new HashMap<String, String>();
+        
+        String userGroupCode = sms.substring( 0, sms.indexOf( " " ) );
+        String content = sms.substring( userGroupCode.length());
+        
+        output.put( userGroupCode.trim(), content.trim() );
+        
+        return output;
+    }
+
+    @Override
+    public void setSeparator( String separator )
+    {
+    }
+}

=== added file 'dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/J2MEDataEntryParser.java'
--- dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/J2MEDataEntryParser.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/J2MEDataEntryParser.java	2013-07-16 11:33:35 +0000
@@ -0,0 +1,90 @@
+package org.hisp.dhis.sms.parse;
+
+/*
+ * Copyright (c) 2004-2012, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.hisp.dhis.smscommand.SMSCommand;
+
+public class J2MEDataEntryParser
+    implements IParser
+{
+    private SMSCommand smsCommand;
+
+    public J2MEDataEntryParser()
+    {
+    }
+    
+    public J2MEDataEntryParser( SMSCommand smsCommand )
+    {
+        this.smsCommand = smsCommand;
+    }
+    
+    @Override
+    public Map<String, String> parse( String sms )
+    {
+        String[] keyValuePairs = null;
+        
+        if ( sms.indexOf( "#" ) > -1 )
+        {
+            keyValuePairs = sms.split( "#" );
+        }
+        else
+        {
+            keyValuePairs = new String[1];
+            keyValuePairs[0] = sms;
+        }
+
+        Map<String, String> keyValueMap = new HashMap<String, String>();
+        for ( String keyValuePair : keyValuePairs )
+        {
+            String[] token = keyValuePair.split( Pattern.quote( smsCommand.getSeparator() ) );
+            keyValueMap.put( token[0], token[1] );
+        }
+
+        return keyValueMap;
+    }
+
+    @Override
+    public void setSeparator( String separator )
+    {
+        // TODO Auto-generated method stub
+    }
+
+    public SMSCommand getSmsCommand()
+    {
+        return smsCommand;
+    }
+
+    public void setSmsCommand( SMSCommand smsCommand )
+    {
+        this.smsCommand = smsCommand;
+    }
+}

=== added file 'dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/SMSParserKeyValue.java'
--- dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/SMSParserKeyValue.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-sms/src/main/java/org/hisp/dhis/sms/parse/SMSParserKeyValue.java	2013-07-16 11:33:35 +0000
@@ -0,0 +1,51 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.hisp.dhis.sms.parse;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang.StringUtils;
+
+/**
+ * @author Magnus Korvald
+ */
+public class SMSParserKeyValue
+    implements IParser
+{
+    // "(\\w+)\\s*\\*\\s*([\\w ]+)\\s*(\\*|$)*\\s*";
+    // = "([a-zA-Z]+)\\s*(\\d+)";
+    private String defaultPattern = "([a-zA-Z]+)\\s*(\\d+)";
+
+    private Pattern pattern = Pattern.compile( defaultPattern );
+
+    @Override
+    public Map<String, String> parse( String sms )
+    {
+        HashMap<String, String> output = new HashMap<String, String>();
+
+        Matcher m = pattern.matcher( sms );
+        while ( m.find() )
+        {
+            String key = m.group( 1 );
+            String value = m.group( 2 );
+            
+            if ( !StringUtils.isEmpty( key ) && !StringUtils.isEmpty( value ) )
+            {
+                output.put( key.toUpperCase(), value );
+            }
+        }
+
+        return output;
+    }
+
+    public void setSeparator( String separator )
+    {
+        String x = "(\\w+)\\s*\\" + separator.trim() + "\\s*([\\w ]+)\\s*(\\" + separator.trim() + "|$)*\\s*";
+        pattern = Pattern.compile( x );
+    }
+}

=== modified file 'dhis-2/dhis-services/dhis-service-sms/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-services/dhis-service-sms/src/main/resources/META-INF/dhis/beans.xml	2013-07-16 06:53:26 +0000
+++ dhis-2/dhis-services/dhis-service-sms/src/main/resources/META-INF/dhis/beans.xml	2013-07-16 11:33:35 +0000
@@ -55,11 +55,14 @@
 
   <bean id="org.hisp.dhis.sms.parse.DefaultParserManager" class="org.hisp.dhis.sms.parse.DefaultParserManager"
     depends-on="org.hisp.dhis.user.UserService">
+    <property name="userService" ref="org.hisp.dhis.user.UserService" />
+    <property name="smsCommandService" ref="smsCommandService" />
+    <property name="dataValueService" ref="org.hisp.dhis.datavalue.DataValueService" />
     <property name="outboundSmsService" ref="org.hisp.dhis.sms.outbound.OutboundSmsService" />  
-    <property name="dataValueSMSListener" ref="org.hisp.dhis.sms.DataValueSMSListener" />
-    <property name="j2meDataValueSMSListener" ref="org.hisp.dhis.sms.J2MEDataValueSMSListener" />
-    <property name="unregisteredSMSListener" ref="org.hisp.dhis.sms.UnregisteredSMSListener" />
-    <property name="dhisMessageAlertListener" ref="org.hisp.dhis.sms.DHISMessageAlertListener" />
+    <property name="registrationService" ref="org.hisp.dhis.dataset.CompleteDataSetRegistrationService" />
+    <property name="messageConversationStore" ref="org.hisp.dhis.message.MessageConversationStore" />
+    <property name="smsMessageSender" ref="org.hisp.dhis.sms.outbound.SmsSender" />
+    <property name="emailMessageSender" ref="emailMessageSender" />
   </bean>
   
 </beans>