Please refer to the internal class files of $XPUSH_HOME/lib/xpush-3.0.x.jar and $XPUSH_HOME/lib/nimbus-1.1.9.jar to develop a user-defined interface.
Authenticator Development
X-PUSH requires authentication for three external service connections.
Nexacro Client access
Message Provider access
Monitor access
Each service responsible for external connection requests actually requests authentication from external authenticators. The developer must develop this class directly, because the authentication methods and logic are different for each system.
The class responsible for authentication must implement the com.nexacro.xpush.fw.service.auth.Authenticator interface, and the class must be specified in the configuration file. If authentication is required, the corresponding class is loaded, and then authentication is requested.
The set class is generated into a jar file and placed in $XPUSH_HOME/lib. The installed jar must contain all the resources used by the implemented authenticator.
All Authenticators of the deployment is set as com.nexacro.xpush.fw.service.auth.DummyAuthenticator as the default. Other authenticator classes that can be used are as follows.
Class Name | Description |
---|---|
DummyAuthenticator | All users are allowed to access |
UserPropertiesAuthenticator | Users registered in $XPUSH_HOME/conf/user.properties file are allowed |
UserPropertiesEncryptAuthenticator | Users with encrypted values registered in $XPUSH_HOME/conf/user.properties file are allowed |
UserPropertiesAuthenticator authenticates using the /conf/user.properties file. Users not registered in user.properties will fail to log in after generating AuthenticateException.
User setting method is "User ID"="User PASSWORD".
UserPropertiesEncryptAuthenticator class can perform user authentication by entering an encrypted password in $XPUSH_HOME/conf/user.properties.
Please refer to the user.properties password encryption for more information on password encryption.
Authenticator interface
The com.nexacro.xpush.fw.service.auth.Authenticator interface provided by X-PUSH declares the authenticate() method for authentication requests and the logout() method for notifying the existing system when a user logs out.
Authenticator.authenticate()
public UserProfile authenticate (String userId, String password) throws AuthenticateException, AuthenticateSystemException
Authentication is handled with the three transmitted strings. AuthenticateException is thrown if the authentication itself is unsuccessful, and AuthenticateSystemException is thrown for system errors such as database connection failures. If authentication is successful, the com.nexacro.xpush.fw.service.auth.UserProfile is returned as an inherited class. An X-PUSH Server does not change the returned UserProfile and delivers it when logout is called by the Message Formatter and authenticator. If additional information is needed to process the Message Formatter or logouts, the class that inherited the UserProfile is defined and returned.
Parameters | |
---|---|
projectId | User projectId |
userId | User ID |
password | User password |
Return value | |
---|---|
UserProfile | Class that inherited the UserProfile class. |
Exception | |
---|---|
AuthenticateException | Occurs when authentication fails. |
AuthenticateSystemException | Triggers when an error or failure occurs in the authentication system. |
Authenticator.logout()
public void logout (UserProfile userProfile) throws AuthenticateSystemException
If a client breaks a connection, the X-PUSH Server calls the logout method by setting the UserProfile as a parameter. An X-PUSH Server only calls logout() to notify the client's connection termination to an external system. Therefore, the execution result of logout() does not affect the operation of the X-PUSH Server.
Parameters | |
---|---|
UserProfile | Class containing user information. |
Exception | |
---|---|
AuthenticateSystemException | Triggers when an error or failure occurs in the authentication system. |
Example
The DummyAuthenticator is a class with only the authenticator interface implemented. The authenticate() and logout() methods do not process any logic with respect to transmitted parameters. As a result, all inputs are handled as being authenticated.
import com.nexacro.xpush.fw.service.auth.AuthenticateException; import com.nexacro.xpush.fw.service.auth.AuthenticateSystemException; import com.nexacro.xpush.fw.service.auth.Authenticator; import com.nexacro.xpush.fw.service.auth.UserProfile; public class DummyAuthenticator implements Authenticator { public UserProfile authenticate(String projectID, String userID, String userPW) throws AuthenticateException, AuthenticateSystemException { UserProfile userProfile = null; userProfile = new UserProfile(projectID, userID, userPW); System.out.println("projectID : " + projectID); System.out.println("userID : " + userID); System.out.println("userPW : " + userPW); return userProfile; } public void logout(UserProfile arg0) throws AuthenticateSystemException { System.out.println("logout function"); } }
Authenticator Installation
Place authenticator class that implemented the authenticator interface and other resources (other classes, property files) that require authenticators into the "$XPUSH_HOME/lib/classes" directory. Or, package them in a jar file, and place it into the "$XPUSH_HOME/lib" directory. When starting X-PUSH, all jar files located in "$XPUSH_HOME/lib" are included in the class path to be used.
Authenticator Setup
The MiPlatformReliabilityNettyWithProjectIDProtocol, SocketProviderProtocol, and MonitorProtocol services load the configured classes and request authentication when required. The authentication classes developed for this purpose are set in the X-PUSH configuration file "$XPUSH_HOME/conf/xpush_config.xml"
The developed authenticator class settings are as follows:
MiPlatformProtocolReliabilityAuthenticator service setup
<service name="MiPlatformProtocolReliabilityAuthenticator" ...> <attribute name="AuthenticatorClassName"> com.nexacro.xpush.fw.service.auth.UserPropertiesReliabilityAuthenticator </attribute> </service>
SocketProviderProtocolAuthenticator service setup
<service name="SocketProviderProtocolAuthenticator" ...> <attribute name="AuthenticatorClassName"> com.nexacro.xpush.fw.service.auth.UserProfileDummyAuthenticator </attribute> </service>
MonitorProtocol service setup
<service name="MonitorProtocol" ...> <attribute name="AuthenticatorServiceName">#MonitorProtocolAuthenticator</attribute> <depends> <service name="MonitorProtocolAuthenticator" code="com.nexacro.xpush.service.auth.AuthenticatorService"> <attribute name="AuthenticatorClassName"> com.nexacro.xpush.fw.service.auth.UserPropertiesEncryptAuthenticator </attribute> </service> </depends> <depends>Log</depends> </service>
UserPropertiesAuthenticator authenticates using /conf/user.properties. Password is encrypted and users not registered in user.properties will fail to log in after generating AuthenticateException.
The set class is generated into a jar file and placed in $XPUSH_HOME/lib. The installed jar must contain all the resources used by the implemented authenticator.
All Authenticators of the deployment is set as com.nexacro.xpush.fw.service.auth.DummyAuthenticator as the default. Other authenticator classes that can be used are as follows.
Class Name | Description |
---|---|
DummyAuthenticator | All users are allowed to access |
UserPropertiesRealiabilityAuthenticator | All users are allowed to access |
UserPropertiesAuthenticator | Users registered in $XPUSH_HOME/conf/user.properties file are allowed |
UserPropertiesEncryptAuthenticator | Users with encrypted values registered in $XPUSH_HOME/conf/user.properties file are allowed |
UserPropertiesAuthenticator authenticates using the /conf/user.properties file. Users not registered in user.properties will fail to log in after generating AuthenticateException.
User setting method is "User ID"="User PASSWORD".
UserPropertiesEncryptAuthenticator class can perform user authentication by entering an encrypted password in $XPUSH_HOME/conf/user.properties.
Please refer to the user.properties password encryption for more information on password encryption.
Embedded Message Provider Development
The aforementioned Message Provider must be developed as an application to operate independently of an X-PUSH Server. Using the Embedded Message Provider, however, messages can be provided directly within an X-PUSH Server. As an X-PUSH Server is a Java implementation, the Embedded Message Provider must be developed in Java as well.
To use the Embedded Message Provider feature, the Actor class that actually provides messages must be developed, and it must be configured in "xpush_config.xml."
After starting, an X-PUSH Server loads the configured Actor class and calls the start() method. In addition, the stop() method is called at the time of shutdown.
Actor Class
Implement a class that inherits the "com.nexacro.xpush.api.EmbeddedMessageProviderActor" abstract class.
The methods to be implemented by the inherited class are start() and stop().
void start()
Void start() is called once at the time of X-PUSH Server startup. The start() method must be returned immediately after processing message provision. Otherwise, the X-PUSH Server will prevent further processing. Therefore, the thread for providing messages must be created in the start () method and must be returned after startup.
An X-PUSH Server will not operate if the start() method is blocked.
void stop()
Void stop() is called once at the time of X-PUSH Server shutdown.
Example
The following sample code pushes the current time in a message once every second. The example shows operating by creating a thread from the start() method and using the providerMessage(PushMessage) method to provide messages.
Import java.io.UnsupportedEncodingException; import java.text.SimpleDateFormat; import java.util.Date; import com.nexacro.xpush.api.EmbeddedMessageProviderActor; import com.nexacro.xpush.fw.service.log.LogLevel; import com.nexacro.xpush.fw.service.provider.PushMessage; public class UserMessageProviderActor extends EmbeddedMessageProviderActor { Thread thread = null; public void start() { thread = new MessageGeneratorThread(); thread.start(); } public void stop() { ((MessageGeneratorThread)thread).stopRunning(); } public class MessageGeneratorThread extends Thread { private boolean isRunning = true; public void stopRunning() { isRunning = false; } public void run() { while(isRunning) { try { PushMessage pushMessage = new PushMessage(); pushMessage.setEncoding("utf-8"); SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss"); String currentTime = "PUSHED CURRENT TIME : " + formatter.format(new Date()); pushMessage.setActionType( Constants.ACTION_PUSH_STRING);// Action Type pushMessage.setProjectID("PRO#1"); // Project ID pushMessage.setTopicType("OPDT ");// Topic Type pushMessage.setTopicId ("ALL ");// Topic Id pushMessage.addData("ALL");// data - value pushMessage.addData(currentTime);// data - value logger.write(LogLevel.DEBUG, "message provided"); provideMessage(pushMessage); sleep(1000); } catch (InterruptedException e) { logger.write(LogLevel.ERROR, "embedded message provider : exception occured.", e); } catch (UnsupportedEncodingException e) { logger.write(LogLevel.ERROR, "embedded message provider : exception occured.", e); } } } } }
Log Processor
There may be times when a log has to be directly received and processed. The LogDispatcherService is just the service for delivering the log directly.
The procedure for handling the service is as follows:
Declare the class that implements the interface LogProcessor.
public class LogProcessorSample implements LogProcessor {
Register itself in the LogDispatcherService at a constructor or in a suitable location.
LogDispatcherService.register(this);
Implement the defined processLog() method in the interface LogProcessor.
public void processLog(String log) { LogEvent event = LogParser.ParseLogIntoLogEvent(log); System.out.println(event); }
In the following example, the log is received from the Embedded Message Provider and output directly to the console. The UserMessageProviderActor from the example above was modified, and only the code for the LogProcessor was extracted.
import com.nexacro.xpush.api.EmbeddedMessageProviderActor; import com.nexacro.xpush.service.logDispatch.LogProcessor import com.nexacro.xpush.service.logDispatch.LogDispatcherService public class LogProcessorSample extends EmbeddedMessageProviderActor implements LogProcessor { public void start() { ... LogDispatcherService.register(this); } public void processLog(String log) { System.out.println(log); } }
Installation
Place the class that inherited the EmbeddedMessageProviderActor and other required resources (other classes, property files) into the "$XPUSH_HOME/lib/classes" directory. Or, package them in a jar file, and place it into the "$XPUSH_HOME/lib" directory. When starting X-PUSH, all jar files located in "$XPUSH_HOME/lib" are included in the class path to be used.
Setup
Set the class that inherited the EmbeddedMessageProviderActor in the EmbeddedMessageProviderActorClassName attribute of the EmbeddedPushMessageProvider service in "$XPUSH_HOME/conf/xpush_config.xml"
<service name="EmbeddedPushMessageProvider" ...> <attribute name="QueueServiceNames”>#PublisherPushMessageQueue</attribute> <attribute name="EmbeddedMessageProviderActorClassName"> EmbeddedPushMessageProviderActorSampleWithProjectID </attribute> ... </service>
Even with EmbeddedMessageProvider that does not do anything, the preparation is operated if it is set up. It should not be set and left blank when not in use.
Message Formatter Development
The Message Formatter can be used to send different messages to each client or to filter messages per client. For example, when names of clients such as "Mr./Mrs. XXX..." need to be included in messages, the message provider does not transmit multiple messages with each individual client's name. Rather, the name portion of a message is substituted with the corresponding client name before a message is transmitted.
An X-PUSH Server calls the Message Formatter's format () method just before transmitting a message to a client. At this time, a client's UserProfile, message type, message ID, and message contents are included a list (byte[] format) and transmitted. The transmitted UserProfile is an object returned by the authenticator's authenticate() method. The message type and ID are provided by the message provider, and the list contains the delivered bit stream in a byte[] format. The byte[] values contained in a list are values passed from the message provider API to the pushMessage.addData(). If two addData() values were called, the size of the list is 2 (List.size()), and there will be two byte[] values.
If a value returned by the Message Formatter's format() method is null, an X-PUSH Server does not push the respective message to the client. By taking advantage of this method, a filtering function can be implemented that determines whether messages are transmitted via a UserProfile's property value. By using appropriate logic, you can also implement a group messaging function that pools multiple messages to be transmitted at once.
Message Formatter Interface
Define a class that implements the "com.nexacro.xpush.fw.service.publish.MessageFormatter" interface.
X-PUSH server calls the format() method of MessageFormatter set right before sending a message to each client. MessageFormatter can convert the content of the message using the UserProfile passed as a parameter or filter the message according to the UserProfile.
MessageFormatter.format()
public List<byte[]> format(UserProfile userProfile, final String topicType, final String topicId, List<byte[]> valueBytesList)
The UserProfile returned as a result of authentication as well as the message topicType, topicId, and contents are contained in a list in the byte[] format, and they are called as parameters by an X-PUSH Server just before transmitting a message to a client. The byte[] contained in a returned list is actually delivered to the client. If a returned value is null, the respective message is not pushed to the client.
Parameters | |
---|---|
userProfile | Object returned via the authenticator's authenticate() method. |
topicType | Message type |
topicId | Message key |
valueBytesLists | List containing message values in byte[] formats. |
Return value | |
---|---|
List<byte[]> | List containing formatted values in byte[] formats. |
Example
In the following example, NameMessagFormatter.java decodes the values transmitted in byte[] formats to UTF-8, and then substitutes "userName" values with those from userProfile.getUserName(). Substituted strings are re-encoded into UTF-8.
$r_title(NameMessageFormatter.java) NameMessageFormatter.javaimport java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Date; import java.util.List; import com.nexacro.xpush.fw.service.auth.UserProfile; import com.nexacro.xpush.fw.service.publish.MessageFormatter; public class NameMessageFormatter implements MessageFormatter { public List<byte[]> format(UserProfile userProfile, final String topicType, final String topicId, List<byte[]> valueBytesList) { List<byte[]> formattedValueBytesList = new ArrayList<byte[]>(); String userName = (MyUserProfile(userProfile)).getUserName(); for(int i=0; i<valueBytesList.size(); i++) { byte[] valueBytes = valueBytesList.get(i); byte[] formattedValueBytes = new byte[0]; try { String value = new String(valueBytes, "utf-8"); String formattedValue = value.replace("userName", userName); formattedValueBytes = formattedValue.getBytes("utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } formattedValueBytesList.add(formattedValueBytes); } return formattedValueBytesList; } }
$r_title(MyUserProfile.java) import com.nexacro.xpush.fw.service.auth.UserProfile; public class MyUserProfile extends UserProfile { private String userName; public void setUserName(String userName) { this.userName = userName; } public String getUserName() { return userName; } }
In the following example, the DummyMessageFormatter does not perform any conversions. The DummyMessageFormatter is set to a default value at the time of deployment.
$r_title(DummyMessageFormatter.java) import java.util.List; import com.nexacro.xpush.fw.service.auth.UserProfile; import com.nexacro.xpush.fw.service.publish.MessageFormatter; public class DummyMessageFormatter implements MessageFormatter { public List<byte[]> format(UserProfile userProfile, final String topicType, final String topicId, List<byte[]> valueBytesList) { return valueBytesList; } }
Installation
Place the class that implemented the Message Formatter and other required resources (other classes, property files) into the "$XPUSH_HOME/lib/classes" directory. Or, package them in a jar file, and place it into the "$XPUSH_HOME/lib" directory. When starting X-PUSH, all jar files located in "$XPUSH_HOME/lib" are included in the class path to be used.
Setup
Set the class defined under the MessageFormatterClassName attribute in the MessageFormatterFactory service in the "xpush_config.xml" configuration file. An X-PUSH Server dynamically loads classes.
<service name="PublishContainerFactory"> ... <attribute name="MessageFormatterClassName"> MyMessageFormatter </attribute> ... </service>
The Message Formatter is used whenever messages are sent to a client. Inadequate internal logic in the Message Formatter can have a direct impact on the performance of an X-PUSH Server.
If the Message Formatter is set up, preparatory work must be completed regardless of whether it's being used. If not being used, do not apply the settings and leave the item blank.
Received Message Processor Development
ReceivedMessageProcessor interface
Define the class that implements the "com.nexacro.xpush.api.ReceivedMessageProcessor" interface.
X-PUSH server calls the receive() method of ReceivedMessageProcessor after receiving a message from each provider. Records of sent messages can be logged or other follow-up actions can be processed using ReceivedMessageProcessor.
ReceivedMessageProcessor.receive()
public void receive(PushMessage pushmessage)
The function is called just before a message received from the provider is inserted into the main queue.
A push message that is to be transmitted when called contains the message topicType, topicId, and contents in a list in the byte[] format. This is the same format as a push message that is transmitted from a provider.
Parameters | |
---|---|
pushmessage | Push message received by X-PUSH from a provider. |
Example
In the following example, ReceivedMessageProcessorSampleClass.java outputs the contents of a received PushMessage to the screen.
$r_title(ReceivedMessageProcessorSample.java) import com.nexacro.xpush.api.ReceivedMessageProcessor; import com.nexacro.xpush.fw.service.provider.PushMessage; public class ReceivedMessageProcessorSample implements ReceivedMessageProcessor { public void receive(PushMessage receivedMessage) { try { File file = new File("./ReceivedMessageList.txt"); FileWriter fileWriter = new FileWriter(file, true); StringBuffer sb = new StringBuffer() .append(" Topic Type: ") .append(receivedMessage.getTopicType()) .append(" Topic ID: ").append(receivedMessage.getTopicId()); for (byte[] value : receivedMessage.getValueBytesList()) { sb.append(" Value: ").append(new String(value)); } fileWriter.write(sb.toString()); fileWriter.close();} catch (IOException e) { e.printStackTrace(); } } }
In the following example, the DummyReceivedMessageProcessor does not perform any operations.
$r_title(DummyReceivedMessgeProcessor.java) import com.nexacro.xpush.api.ReceivedMessageProcessor; import com.nexacro.xpush.fw.service.provider.PushMessage; public class DummyReceivedMessageProcessor implements ReceivedMessageProcessor { public void receive(PushMessage receivedMessage) { } }
Installation
Place the class that implemented the ReceivedMessageProcessor and other required resources (other classes, property files) into the "$XPUSH_HOME/lib/classes" directory. Or, package them in a jar file, and place it into the "$XPUSH_HOME/lib" directory. When starting X-PUSH, all jar files located in "$XPUSH_HOME/lib" are included in the class path to be used.
Setup
Set the class defined under the ReceivedMessageProcessorClassName attribute in the SocketPushMessageProvider service in the "xpush_config.xml" configuration file. An X-PUSH Server dynamically loads classes.
<service name="SocketPushMessageProvider"> ... <attribute name="ReceivedMessageProcessorClassName"> ReceivedMessageProcessorSample </attribute> ... </service>
Even with ReceivedMessageProcessor that does not do anything, the preparation is operated if it is set up. It should not be set and left blank when not in use.
Pushed Message Processor Development
The Pushed Message Processor can be used to save records of sent messages or process other follow-up operations. For example, messages that were transmitted normally or abnormally can be stored in a file or database so that the administrator can identify which message is not being transmitted to a client for follow-up activities.
An X-PUSH Server calls the PushedMessageProcessor's process() method immediately after transmitting a message to a client. At this time, a client's UserProfile, message type, message ID, and message contents are included a list (byte[] format) and transmitted. The transmitted UserProfile is an object returned by the authenticator's authenticate() method. The message type and ID are provided by the message provider, and the list contains the delivered bit stream in a byte[] format. The byte[] values contained in a list are values passed from the message provider API to the pushMessage.addData() from messages actually delivered to clients. If two addData() values were called, the size of the list is 2 (List.size()), and there will be two byte[] values. If a message was modified through the use of the Message Formatter, the modified value will be contained in the list.
PushedMessageProcessor interface
Define the class that implements the "com.nexacro.xpush.fw.service.publish.PushedMessageProcessor" interface.
PushedMessageProcessor.process
If a message was transmitted normally:
public void process(UserProfile userProfile, final String action, final String topicType, final String topicId, String messageID, List<byte[]> valueBytesList)
The UserProfile returned as a result of authentication as well as the message topicType, topicId, messageID, and contents are contained in a list in the byte[] format, and they are called as parameters by an X-PUSH Server after transmitting a message to a client.
Parameters | |
---|---|
userProfile | Object returned via the authenticator's authenticate() method. |
action | Message type (PUSH/RELI) |
topicType | Message type |
topicId | Message key |
messageID | Message ID |
valueBytesLists | List containing message values in byte[] formats. |
If Action is PUSH, messageID is NULL.
If a message was not transmitted normally:
public void process(UserProfile userProfile, final String action, final String topicType, final String topicId, String messageID, List<byte[]> valueBytesList , Exception exception)
The UserProfile returned as a result of authentication as well as the message topicType, topicId, and contents are contained in a list in the byte[] format, and they are called as parameters by an X-PUSH Server when an error occurs at the time of transmitting a message to a client.
Parameters | |
---|---|
userProfile | Object returned via the authenticator's authenticate() method. |
topicType | Message type |
topicId | Message key |
messageID | Message ID |
valueBytesLists | List containing message values in byte[] formats. |
Exception | Exception for failed message transmission contents. |
Example
In the following example, DemoPushedMessageProcessor.java stores the contents of successful and failed message transmissions in files.
$r_title(DemoPushedMessageProcessor.java) import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.util.List; import com.nexacro.xpush.fw.service.auth.UserProfile; import com.nexacro.xpush.fw.service.publish.PushedMessageProcessor; public class TestPushedMessageProcessor implements PushedMessageProcessor{ public void process(UserProfile userProfile, String action, String topicType,String topicId, List<byte[]> valueBytesList) { try { File file = new File("./SuccessMessageList.txt"); FileWriter fileWriter = new FileWriter(file); StringBuffer sb = new StringBuffer().append("SUCCESS USER ID :") .append(userProfile.getId()).append("projectID:") .append(userProfile.getProjectID()) .append("Topic Type :") .append(topicType) .append(" Action:").append(action) .append(" Topic ID:").append(topicId) .append(" Meesage ID:").append(messageID).append("/n"); fileWriter.write(sb.toString()); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } public void process(UserProfile userProfile, String action, String topicType, String topicId, String messageID, List<byte[]> valueBytesList, Exception exception) { try { File file = new File("./FailMessageList.txt"); FileWriter fileWriter = new FileWriter(file); StringBuffer sb = new StringBuffer().append("FAIL USER ID :") .append(userProfile.getId()).append("projectID:") .append(userProfile.getProjectID()) .append(" Topic Type :").append(topicType) .append(" Action:").append(action) .append(" Topic ID:").append(topicId).append("/n") .append(" Meesage ID:").append(messageID).append("/n") .append("Exception Message :") .append(exception.getMessage()); fileWriter.write(sb.toString()); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } public void process(UserProfile userProfile, String topicType,String topicId, List<byte[]> valueBytesList) { try { File file = new File("./SuccessMessageList.txt"); FileWriter fileWriter = new FileWriter(file); StringBuffer sb = new StringBuffer().append("SUCCESS USER ID :") .append(userProfile.getId()).append("Topic Type :") .append(topicType) .append(" Topic ID:").append(topicId); fileWriter.write(sb.toString()); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } public void process(UserProfile userProfile, String topicType, String topicId, List<byte[]> valueBytesList, Exception exception) { try { File file = new File("./FailMessageList.txt"); FileWriter fileWriter = new FileWriter(file); StringBuffer sb = new StringBuffer().append("FAIL USER ID :") .append(userProfile.getId()).append(" Topic Type :") .append(topicType) .append(" Topic ID:").append(topicId).append("/n") .append("Exception Message :") .append(exception.getMessage()); fileWriter.write(sb.toString()); fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } }
In the following example, the DummyPushedMessageProcessor does not perform any operations.
$r_title(DummyPushedMessgeProcessor.java) import java.util.List; import com.nexacro.xpush.fw.service.auth.UserProfile; import com.nexacro.xpush.fw.service.publish.PushedMessageProcessor; public class DummyPushedMessageProcessor implements PushedMessageProcessor { public void process(UserProfile userProfile, final String action, final String topicType, final String topicId, String messageID, List<byte[]> valueBytesList) { } public void process(UserProfile userProfile, final String action, final String topicType, final String topicId, String messageID, List<byte[]> valueBytesList , Exception exception) { } public void process(UserProfile userProfile, final String topicType, final String topicId, List<byte[]> valueBytesList) { } public void process(UserProfile userProfile, final String topicType, final String topicId, List<byte[]> valueBytesList , Exception exception) { } }
Installation
Place the class that implemented the PushedMessageProcessor and other required resources (other classes, property files) into the "$XPUSH_HOME/lib/classes" directory. Or, package them in a jar file, and place it into the "$XPUSH_HOME/lib" directory. When starting X-PUSH, all jar files located in "$XPUSH_HOME/lib" are included in the class path to be used.
Setup
Set the class defined under the PushedMessageProcessorClassName attribute in the PublishContainerFactory service in the "xpush_config.xml" configuration file. An X-PUSH Server dynamically loads classes.
<service name="PublishContainerFactory"> ... <attribute name="PushedMessageProcessorClassName"> MyPushedMessageProcessor </attribute> ... </service>
The PushedMessageProcessor is used whenever messages are sent to a client. Inadequate internal logic in the PushedMessageProcessor can have a direct impact on the performance of an X-PUSH Server.
If the PushedMessageProcessor is set up, preparatory work must be completed regardless of whether it's being used. If not being used, do not apply the settings, and leave the item blank.
Notification Formatter Development
The NotificationFormatter can set the contents of a notification that is transmitted when a mobile client is not connected to an X-PUSH Server. To include an additional message to a notification of a particular topic, for example, you can add user parameters using the NotificationFormatter.
An X-PUSH Server transmits a received message to the NotificationBuilderService, and if a mobile client is not connected to the X-PUSH Server, the NotificationBuilderService generates a notification to be transmitted. At this time, the NotificationBuilderService calls the format() method from the NotificationFormatter. In the case of reliable messages, the format() method with PushMessage as a parameter is called.
NotificationFormatter Abstract Class Implementation
Implement a class that inherits the "com.nexacro.xpush.service.notification.NotificationFormatter" abstract class.
NotificationFormatter.format()
public NotificationPayload format(PushMessage pushMessage, NotificationPayload payload)
The function is called if a message received from a provider needs to be transmitted to a client as a notification.
A push message that is transmitted when called contains the messageType, messageId, and message contents in a list in the byte[] format. This is the same format as a push message that is transmitted from a provider.
The contents of a notification to be delivered to a client for NotificationPayload.
Parameters | |
---|---|
pushmessage | Push message received by X-PUSH from a provider. |
payload | A notification to be transmitted to a mobile device by X-PUSH. |
Example
In the following example, UserNotificationFormatter.java sets the properties of a received push message in NotificationPayload and adds user parameters.
$r_title(UserNotificationFormatter.java) import com.nexacro.xpush.fw.service.provider.MobileNotification; import com.nexacro.xpush.fw.service.provider.PushMessage; import com.nexacro.xpush.service.notification.NotificationFormatter; import com.nexacro.xpush.service.notification.type.NotificationPayload; public class UserNotificationFormatter extends NotificationFormatter { public NotificationPayload format(PushMessage pushMessage, NotificationPayload payload) { String action = pushMessage.getActionType(); payload.setCommand(action); String topicType = pushMessage.getTopicType(); String topicId = pushMessage.getTopicId(); payload.addParam(NotificationPayload.TOPIC_TYPE, topicType); payload.addParam(NotificationPayload.TOPIC_ID, topicId); String message_0 = null; String message_1 = null; try { message_0 = new String(pushMessage.getValueBytesList().get(0), "UTF-8"); message_1 = new String(pushMessage.getValueBytesList().get(1), "UTF-8"); } catch (UnsupportedEncodingException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } payload.addParam("NAME", message_0 ); payload.addParam("PHONE", message_1 ); payload.setMessage( message_0 + "-custom-" + message_0 ); if (payload.isTooBig()) { NotificationPayload summarized = summarize(payload); return summarized; } return payload; } }
The following example shows the default implementation of NotificationFormatterSample.java setting the properties of a received push message in NotificationPayload.
$r_title(NotificationFormatterBasicImpl.java) package com.nexacro.xpush.service.notification; import com.nexacro.xpush.fw.service.provider.MobileNotification; import com.nexacro.xpush.fw.service.provider.PushMessage; import com.nexacro.xpush.service.notification.NotificationFormatter; import com.nexacro.xpush.service.notification.type.NotificationPayload; public class NotificationFormatterBasicImpl extends NotificationFormatter { public NotificationPayload format(PushMessage pushMessage, NotificationPayload payload) { String action = pushMessage.getActionType(); payload.setCommand(action); String topicType = pushMessage.getTopicType(); String topicId = pushMessage.getTopicId(); payload.addParam(NotificationPayload.TOPIC_TYPE, topicType); payload.addParam(NotificationPayload.TOPIC_ID, topicId); if (payload.isTooBig()) { NotificationPayload summarized = summarize(payload); return summarized; } return payload; } }
$r_title(NotificationFormatterCustom.java) import com.nexacro.xpush.fw.service.provider.MobileNotification; import com.nexacro.xpush.fw.service.provider.PushMessage; import com.nexacro.xpush.service.notification.NotificationFormatter; import com.nexacro.xpush.service.notification.type.NotificationPayload; public class NotificationFormatterCustom extends NotificationFormatter{ public NotificationPayload format(PushMessage pushMessage, NotificationPayload payload) { payload.setMessage( "data message\n:" + pushMessage.getDataArray()[0] + "\n:" + pushMessage.getDataArray()[1] + "\n:" + pushMessage.getDataArray()[2] ); if (payload.isTooBig()) { NotificationPayload summarized = summarize(payload); return summarized; } return payload; } public NotificationPayload format(MobileNotification arg0, NotificationPayload arg1) { return null; } }
Installation
Place the class that implemented the NotificationFormatter and other required resources (other classes, property files) into the "$XPUSH_HOME/lib/classes" directory. Or, package them in a jar file, and place it into the "$XPUSH_HOME/lib" directory. When starting X-PUSH, all jar files located in "$XPUSH_HOME/lib" are included in the class path to be used.
Setup
Set the class defined under the NotificationFormatterName attribute in the NotificationBuilderService service in the "xpush_config.xml" configuration file. An X-PUSH Server dynamically loads classes.
<service name="NotificationBuilderService"> ... <attribute name="NotificationFormatterName"> UserNotificationFormatter </attribute> ... </service>
Note that the "NotificationFormatterBasicImpl" under "NotificationFormatterName" must be configured even if you do not use the NotificationFormatter with specific settings.
The push message - transmitted as a parameter via the NotificationFormatter's format() method call - is a copy of the message that is used internally in X-PUSH. The modification of the push message has no effect on the contents of a message being delivered to a client. This is true for mobile notifications as well.
Pushed Notification Processor Development
Pushed Notification Processor can log a record of delivered notifications or process other follow-up actions. For example, notifications that are delivered normally or abnormally are stored in DB or file so that the administrator can understand which messages are not delivered to which clients and proceed with the follow-up actions.
X-PUSH server calls the process() method of PushedNotificationProcessor set right after sending the notification to the client. At this time, the result, which is the result data of the notification sent to the client, Type and id of Topic, and Notification Payload data included in the notification are included.
PushedNotificationProcessor Interface
PushedNotificationProcessor interface exists as a different interface according to GCM and APNS.
In the case of GCM, the class that implemented PushedGcmNotificationProcessor must be defined, and in the case of APNS, the class that implemented PushedApnsNotificationProcessor must be defined.
PushedGcmNotificationProcessor
public void multiAppProcess(GcmResult result, Topic topic, NotificationPayload payload)
The follow-up action can be performed through GcmResult returned as the notification result, Topic information related to the notification content, and NotificationPayload containing the notification content.
Parameters | |
---|---|
result | The object containing the notification result 1) errorCode: Notification result 2) message: Transmitted message 3) messageID: messageID of the reliable message 4) registrationID: Device token value 5) updateRegistrationID: When sent with a device token not registered in GCM 6) bundleID: Sender ID where the device token is registered |
topic | The object containing the topic type and topic ID |
payload | The object containing the payload of the notification |
The notification result can be checked through the errorCode of GcmResult. If the errorCode is Null, this is the result of the notification being successfully performed to the client.
The ERROR code can be one of the following values.
FCM Status Code | |
---|---|
NoErrorsEncountered | No error occurred. Transmission successful |
BadApiKey | Incorrect API Key |
MissingRegistration | No Registration ID in the message sent to GCM |
InvalidRegistration | Incorrect Registration ID format |
MismatchSenderId | No Sender ID matching the corresponding Registration ID |
NotRegistered | Registration ID not registered in GCM |
MessageTooBig | Message size exceeding 4KB |
InvalidDataKey | FCM reserved words used for message data key |
InvalidTtl | TTL of the message exceeding the range of 0 ~ 22419200 (4 weeks) |
Unavailable | GCM server unavailable |
InternalServerError | GCM server internal error |
InvalidPackageName | Registration ID indicating invalid package name |
PushedApnsNotificationProcessor
public void multiAppProcess(ApnsResult result, Topic topic, NotificationPayload payload)
The follow-up action can be performed through ApnsResult returned as the notification result, Topic information related to the notification content, and NotificationPayload containing the notification content.
Parameters | |
---|---|
result | The object containing the notification result 1) statusCode: Notification result 2) message: Transmitted message 3) messageID: messageID of the reliable message 4) device: Device token value 5) updateRegistrationID: Device token not registered in APNS or device token value received from the feedback service 6) bundleID: bundleID of the app where the device token is registered |
topic | The object containing topic type and topic ID |
payload | The object containing the payload of the notification |
The notification result can be checked through the statusCode of ApnsResult. If the statusCode is NoErrorEncountered, this is the result of the notification being successfully performed to the client.
The STATUS code can be one of the following values.
Apns Status Code | |
---|---|
NoErrorsEncountered | No error occurred. Transmission successful |
ProcessingError | APNS internal processing error |
MissingDeviceToken | No Device Token in the message sent to APNS |
MissingTopic | No Topic in the message sent to APNS |
MissingPayload | No Payload in the message sent to APNS |
InvalidTokenSize | Device Token size not being 32byte |
InvalidTopicSize | Topic size error |
InvalidPayloadSize | Invalid Device Token |
Shutdown | APNS server Shutdown |
Unknown | Unknown cause |
Examples
The following PushedGcmNotificationProcessorImpl.java is an example of outputting the content of the delivered notification as a log.
$r_title(PushedGcmNotificationProcessorImpl.java) import com.nexacro.xpush.fw.service.log.LogLevel; import com.nexacro.xpush.message.Topic; import com.nexacro.xpush.service.notification.type.GcmResult; import com.nexacro.xpush.service.notification.type.NotificationPayload; import jp.ossc.nimbus.core.ServiceManagerFactory; import jp.ossc.nimbus.service.log.Logger; public class PushedGcmNotificationProcessorImpl implements PushedGcmNotificationProcessor { private Logger logger = ServiceManagerFactory.getLogger(); @Override public void multiAppProcess (GcmResult result, Topic topic, NotificationPayload payload) { logger.write(LogLevel.DEBUG,"Pushed Notification MultiApp GCM Process: " + result.toString() + ", " + topic.toString() + ", " + payload.toString()); }
The following PushedApnsNotificationProcessorImpl is an example of a notification sent to Apns.
$r_title(PushedApnsMessgeProcessor.java) import com.nexacro.xpush.fw.service.log.LogLevel; import com.nexacro.xpush.message.Topic; import com.nexacro.xpush.service.notification.type.ApnsResult; import com.nexacro.xpush.service.notification.type.NotificationPayload; import jp.ossc.nimbus.core.ServiceManagerFactory; import jp.ossc.nimbus.service.log.Logger; public class PushedApnsNotificationProcessorImpl implements PushedApnsNotificationProcessor { private Logger logger = ServiceManagerFactory.getLogger(); @Override public void multiAppProcess (ApnsResult result, Topic topic, NotificationPayload payload) { logger.write(LogLevel.DEBUG,"Pushed Notification MultiApp Apns Process: " + result.toString() + ", " + topic.toString() + ", " + payload.toString()); } }
Installation
Place the class implementing PushedNotificationProcessor and other necessary resources (other class, property file) in $XPUSH_HOME/lib/classes or package it as a jar file and place it in $XPUSH_HOME/lib. When X-PUSH is run, all jar files in $XPUSH_HOME/lib are included in the class path and used.
Settings
Set the class defined in attributes in GcmNotifierService and ApnsNotifierService in the setting file xpush_config.xml. X-PUSH server dynamically loads and uses the corresponding class.
<service name=""GcmNotifierService"> ... <attribute name="PushedGcmNotificationProcessorName"> MyPushedGcmProcessor </attribute> ... </service> ....... <service name=""ApnsNotifierService"> ... <attribute name="PushedApnsNotificationProcessorName"> MyPushedApnsProcessor </attribute> ... </service>
PushedNotificationProcessor is used whenever a notification is sent to the client. Improper logic inside the PushedNotificationProcessor can directly affect the performance of the X-PUSH server.
Even with PushedNotificationProcessor that does not do anything, the preparation is operated if it is set up. It should not be set and left blank when not in use.
Etc Processor Development
EtcProcessor supports the application of SMS or other messenger services through interface implementation when Notification transmission fails.
X-PUSH server calls the process() method of EtcProcessor on the failed notification right after sending it to the client. At this time, a notification message sent to the client, user ID, topic information, and device information can be checked and follow-up actions can be performed accordingly.
EtcProcessor Interface
The class implementing the interface com.nexacro.xpush.service.EtcService.EtcProcessor is defined.
When the requested notification fails
public void notiFailProcess(String messageID, List<String> messages, Topic topic, DeviceInfo deviceInfo) throws ProcessFailException
The following values in the NotificationFailMessage object, which is failList type of the notiFailprocess method.
Parameters | |
---|---|
MessageID | notification messageID of the notification failed to be transmitted (The corresponding message ID can be checked in t_message.) |
messsages | List of messages sent from Provider |
Topic | Topic of messages sent from Provider (topic_type, topic_id) |
DeviceInfo | Device information failed to be transmitted projectID: Project ID userID: User ID token: Device token bundleID: Project, app identifier osVersion: Mobile OS Version os: 1(iOS), 2(Android) errorCode: Failed error code |
The ERROR code is different for FCM and APNS and is one of the following values.
FCM Status Code | |
---|---|
NoErrorsEncountered | No error occurred. Transmission successful |
BadApiKey | Incorrect API Key |
MissingRegistration | No Registration ID in the message sent to GCM |
InvalidRegistration | Incorrect Registration ID format |
MismatchSenderId | No Sender ID matching the corresponding Registration ID |
NotRegistered | Registration ID not registered in GCM |
MessageTooBig | Message size exceeding 4KB |
InvalidDataKey | FCM reserved words used for message data key |
InvalidTtl | TTL of the message exceeding the range of 0 ~ 22419200 (4 weeks) |
Unavailable | GCM server unavailable |
InternalServerError | GCM server internal error |
InvalidPackageName | Registration ID indicating invalid package name |
Apns Error Code | |
---|---|
NoErrorsEncountered | No error occurred. Transmission successful |
ProcessingError | APNS internal processing error |
MissingDeviceToken | No Device Token in the message sent to APNS |
MissingTopic | No Topic in the message sent to APNS |
MissingPayload | No Payload in the message sent to APNS |
InvalidTokenSize | Device Token size not being 32byte |
InvalidTopicSize | Topic size error |
InvalidPayloadSize | Invalid Device Token |
Shutdown | APNS server Shutdown |
Unknown | Unknown cause |
Examples
The following EtcPocessorImpl class is an example of displaying the information of failed Notification. After that, the follow-up action can be performed using other messenger services and SMS of failed notifications.
$r_title(EtcProcessorImpl.java) public class EtcProcessorImpl implements EtcProcessor { private final Logger logger = ServiceManagerFactory.getLogger(); @Override public void notiFailProcess(String messageID, List<String> messages, Topic topic, DeviceInfo deviceInfo) throws ProcessFailException { System.out.println(deviceInfo.getToken()); System.out.println(deviceInfo.getUserID()); System.out.println(deviceInfo.getOs()); System.out.println(deviceInfo.getErrorCode()); System.out.println(messageID); System.out.println(topic.getType()); System.out.println(topic.getId()); // UserID, ERROR_CODE, Device_Token 등을 통해 후속처리 } }
Installation
Place the class implementing EtcProcessor and other necessary resources (other class) in $XPUSH_HOME/lib/classes or package it as a jar file and place it in $XPUSH_HOME/lib. When X-PUSH is run, all jar files in $XPUSH_HOME/lib are included in the class path and used.
If whether to use EtcProcessor has been applied, it is used whenever a notification is sent to the client. Improper logic inside the EtcProcessor can directly affect the performance of the X-PUSH server.
Even with EtcProcessor that does not do anything, the preparation is operated if it is set up. It should not be set and left blank when not in use.
Settings
Set the class defined in attributes in the EtcService item in the setting file xpush_config.xml. X-PUSH server dynamically loads and uses the corresponding class.
<service name="EtcService"> <attribute name="EtcHandlerThreadPoolCount">1</attribute> <attribute name="EtcHandlerProcessingAtOnceCount">1</attribute> .... <attribute name="EtcProcessorName"> com.nexacro.xpush.service.EtcService.EtcProcessorImpl </attribute> </service>
<service name="GcmNotifierService"> ... <attribute name="failOver">true</attribute> ... </service>
<service name="ApnsNotifierService"> ... <attribute name="failOver">true</attribute> ... </service>
To use the EtcProcessor interface, the FailOver attribute values of APNs and FCM must be set to true.
Even with PushedNotificationProcessor that does not do anything, the preparation is operated if it is set up. It should not be set and left blank when not in use.
Crontab Scheduler Development
Task Interface
The class implementing the interface it.sauronsoftware.cron4j.Task is defined.
To further develop the scheduler, the $XPUSH_HOME/lib/cron4j-2.2.5.jar file must be added.
Examples
The following code is a sample code that deletes the normally transmitted message from the T_NOTIFICATION table when the corresponding scheduler is running.
package com.nexacro.xpush.service.schedule; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.text.SimpleDateFormat; import java.util.Calendar; import com.nexacro.xpush.fw.service.log.LogLevel; import com.nexacro.xpush.service.dbConPool.DBCPService; import it.sauronsoftware.cron4j.Task; import it.sauronsoftware.cron4j.TaskExecutionContext; import jp.ossc.nimbus.core.ServiceManagerFactory; import jp.ossc.nimbus.service.log.Logger; /* * Delete notifications past the expiration date every Sunday * * */ public class DeleteExpiredNotificationTask extends Task { private DBCPService dbcpService; private Logger logger = ServiceManagerFactory.getLogger(); private int deleteCount = 0; public DBCPService getDbcpService() { return dbcpService; } public void setDbcpService(DBCPService dbcpService) { this.dbcpService = dbcpService; } @Override public void execute(TaskExecutionContext excute) throws RuntimeException { logger.write(LogLevel.INFO, "Start " + getClass().getName()); try { Calendar cal = Calendar.getInstance(); SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd000000"); String today = format.format(cal.getTime()); findExpiredNotification(today); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void findExpiredNotification(String today) { String sql = " SELECT MESSAGE_ID FROM T_NOTIFICATION "; String sql2 = "SELECT EXPIRATION_DATE FROM T_MESSAGE WHERE MESSAGE_ID = ? "; Connection conn = null; PreparedStatement selectMessageID = null; PreparedStatement selectExpireDate = null; ResultSet messageIdResult = null; ResultSet expireDateResult = null; try { conn = dbcpService.getConnection(); selectMessageID = conn.prepareStatement(sql); messageIdResult = selectMessageID.executeQuery(); while (messageIdResult.next()) { String message_ID = messageIdResult.getString(1); selectExpireDate = conn.prepareStatement(sql2); selectExpireDate.setString(1, message_ID); expireDateResult = selectExpireDate.executeQuery(); // When there is data in the T_MESSAGE table if (expireDateResult.next()) { String expireDate = expireDateResult.getString(1); if (expireDate != null) { if (Long.parseLong(expireDate) <= (Long.parseLong(today))) { close(selectExpireDate, expireDateResult); deleteNotification(conn, message_ID); } } // When there is no data in the T_MESSAGE table (When the corresponding message ID cannot be found in the T_Message table) }else { close(selectExpireDate, expireDateResult); deleteNotification(conn, message_ID); } } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { logger.write(LogLevel.INFO, "Delete Expire Notification Count = " + deleteCount); deleteCount = 0; close(conn, selectMessageID, messageIdResult); } } public void deleteNotification(Connection conn, String messageID) { String sql = " DELETE FROM T_NOTIFICATION WHERE MESSAGE_ID = ? "; PreparedStatement deletetatement = null; try { deletetatement = conn.prepareStatement(sql); deletetatement.setString(1, messageID); int count = deletetatement.executeUpdate(); if (count > 0) { deleteCount += count; } } catch (SQLException e) { // TODO Auto-generated catch block logger.write(LogLevel.ERROR, "SQL = " + sql + ", Message: " + e.getMessage()); try { conn.rollback(); } catch (SQLException e1) { // TODO Auto-generated catch block } } finally { close(deletetatement, null); } } public void close(PreparedStatement psmt, ResultSet rs) { try { if (psmt != null) { psmt.close(); } if (rs != null) { rs.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public void close(Connection conn, PreparedStatement psmt, ResultSet rs) { try { if (rs != null) { rs.close(); } if (psmt != null) { psmt.close(); } if (conn != null) { conn.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Installation
Place the class inheriting Task and other necessary resources (other class, property file) in $XPUSH_HOME/lib/classes or package it as a jar file and place it in $XPUSH_HOME/lib. When X-PUSH is run, all jar files in $XPUSH_HOME/lib are included in the class path and used.
Settings
A scheduler can be added by setting the package path in the object code in the CronTabScheduleService service tag of $XPUSH_HOME/conf/xpush_config.xml for the class that inherits Task. Also, the scheduler cycle can be set.
<service name="CronTabScheduleService" code="com.nexacro.xpush.service.schedule.CronTabScheduleService"> .... <invoke name="add"> <argument type="java.lang.String">0 0 1 * 0</argument> <argument type="it.sauronsoftware.cron4j.Task"> <object code="com.nexacro.xpush.service.schedule.DeleteNotificationMessageTask"> <attribute name="dbcpService"> <service-ref>#DbcpService</service-ref> </attribute> </object> </argument> </invoke> .... </service>