Java client framework / API to call Windows phone

2019-06-25 04:43发布

问题:

Does anyone know if there is a Java client framework / API to call Windows phone push notification service? I know there is a JavaPNS project, which is specific to Apple PNS. I am looking for something similar to that, but specific to Windows phone.

Any help?

回答1:

Java-mpns seems to be close to what you are looking for.

https://github.com/notnoop/java-mpns



回答2:

I've developed a solution with Spring and Apache Commons HTTP Client for sending RAW messages using WNS (Windows Notification Push Service)

Insert this dependencies on your pom.xml:

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.2.1</version>
</dependency>
<dependency>
    <groupId>org.codehaus.jackson</groupId>
    <artifactId>jackson-mapper-asl</artifactId>
    <version>1.9.12</version>
</dependency>

And the following lines on applicationContext.xml

<bean class="java.lang.String" id="authenticateUrlPostWNS">
    <constructor-arg>
        <value>https://login.live.com/accesstoken.srf</value>
    </constructor-arg>
</bean>
<util:map id="authenticateWNSHeaders">
    <entry key="Content-Type" value="application/x-www-form-urlencoded" />
</util:map>
<util:map id="authenticateWNSPostParams">
    <entry key="grant_type" value="client_credentials" />
    <entry key="client_id" value="ms-app://" />
    <entry key="client_secret" value="" />
    <entry key="scope" value="notify.windows.com" />
</util:map>
<util:map id="sendMessageWNSHeaders">
    <entry key="X-WNS-Type" value="wns/raw" />
    <entry key="Content-Type" value="application/octet-stream" />
    <entry key="X-WNS-RequestForStatus" value="true" />
    <entry key="X-NotificationClass" value="3" />
</util:map>

*Remember to fill client_id and client_secret

In my project, I've divided the implementation in four classes, which I'm going to describe below

OAuthToken is used to store and format the access token:

public class OAuthToken {
    private String token;
    private String tokenType;

    public OAuthToken(String token, String tokenType) {
        super();
        this.token = token;
        this.tokenType = tokenType;
    }

    public String getAuthorization() {
        return StringUtils.capitalize(tokenType) + " " + token;
    }
}

The HttpClientFactory (shown bellow) is used to create a new client whenever you send a message. If you reuse the same HttpClient to send a message, WNS time-out after the second message sent. I don't know why that happens, but it solved when I stopped reusing the client.

@Service
public class HttpClientFactory {
    private static final int TIMEOUT = 20 * 1000;

    public HttpClient create() {
        SystemDefaultHttpClient httpClient = new SystemDefaultHttpClient();
        httpClient.getParams().setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, TIMEOUT);
        httpClient.getParams().setIntParameter(CoreConnectionPNames.SO_TIMEOUT, TIMEOUT);
        httpClient.setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(NumberUtils.INTEGER_ZERO, false));
        return httpClient;
    }

}

This is a base class, as you wish, you can pull down all code to WindowsPushNotificationMediator

public abstract class AbstractCloudMediator {
    private static final Logger LOG = Logger.getLogger(AbstractCloudMediator.class.getSimpleName());
    @Autowired
    private HttpClientFactory clientFactory;

    @Autowired
    protected ObjectMapper mapper;

    public abstract boolean sendMessage(Jogador destinatario, Action mensagem);

    protected String postToString(HttpPost post) throws IOException, ClientProtocolException {
        HttpResponse response = executeMethod(post);

        return responseToString(response);
    }

    protected String responseToString(HttpResponse response) throws IOException {
        InputStream conteudoResposta = response.getEntity().getContent();
        try {
            return IOUtils.toString(conteudoResposta);
        } finally {
            IOUtils.closeQuietly(conteudoResposta);
        }
    }

    protected HttpResponse executeMethod(HttpPost post) throws IOException, ClientProtocolException {
        LOG.info("posting to... " + post);
        return clientFactory.create().execute(post);
    }
}

The next class should do the main work, but remember to create a cloudMessagingDAO to retrieve and store your access token. You should replace the class Jogador for another class that contains the client URL, used to send a message to a Windows Phone device.

@Service(SharedConstants.WINDOWS_CLOUD_BEAN)
public class WindowsPushNotificationMediator extends AbstractCloudMediator { // NO_UCD (test only)
    private static final Logger LOG = Logger.getLogger(WindowsPushNotificationMediator.class.getName());
    private static final String KEY_ACCESS_TOKEN = "access_token";
    private static final String KEY_TOKEN_TYPE = "token_type";

    @Resource(name = "authenticateWNSHeaders")
    private Map<String, String> authenticateWNSHeaders;

    @Resource(name = "authenticateWNSPostParams")
    private Map<String, String> authenticateWNSPostParams;

    @Resource(name = "sendMessageWNSHeaders")
    private Map<String, String> sendMessageWNSHeaders;

    @Autowired
    @Qualifier("authenticateUrlPostWNS")
    private String authenticateUrlPostWNS;

    @Autowired
    private CloudMessagingDAO cloudMessagingDAO;

    private OAuthToken oathToken;

    @Override
    public boolean sendMessage(Jogador destinatario, Action mensagem) {
        try {
            OAuthToken token = getToken();
            String jsonString = mapper.writeValueAsString(mensagem);
            StringEntity entity = new StringEntity(jsonString, Consts.UTF_8);
            return sendMessage(destinatario, entity, token);
        } catch (IOException e) {
            LOG.log(Level.SEVERE, e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

    private boolean sendMessage(Jogador destinatario, HttpEntity entity, OAuthToken token) throws IOException {
        HttpPost post = new HttpPost(destinatario.getCloudMessagingInfo());// this is the client url

        post.addHeader("Authorization", token.getAuthorization());
        addPostHeaders(post, sendMessageWNSHeaders);

        post.setEntity(entity);
        HttpResponse response = executeMethod(post);
        if (response.getStatusLine().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
            return sendMessage(destinatario, entity, getNewToken());
        }
        Header[] allHeaders = response.getAllHeaders();
        StringBuilder builder = new StringBuilder();
        for (Header header : allHeaders) {
            builder.append(header.getName() + ": " + header.getValue());
            builder.append('\n');
        }
        LOG.info(builder.toString());
        return response.getStatusLine().getStatusCode() == HttpStatus.SC_OK;
    }

    private void addPostHeaders(HttpPost post, Map<String, String> postHeaders) {
        for (String key : postHeaders.keySet()) {
            post.addHeader(key, postHeaders.get(key));
        }
    }

    private OAuthToken getToken() throws IOException {
        if (oathToken == null) {
            //You should store your access token, so you can reuse it until it expires
            String token = cloudMessagingDAO.getValue(KEY_ACCESS_TOKEN);
            String tokenType = cloudMessagingDAO.getValue(KEY_TOKEN_TYPE);
            if (StringUtils.isNotBlank(token) && StringUtils.isNotBlank(tokenType)) {
                return oathToken = new OAuthToken(token, tokenType);
            }
            return getNewToken();
        }
        return oathToken;
    }

    private OAuthToken getNewToken() throws IOException {
        HttpPost post = new HttpPost(authenticateUrlPostWNS);
        addPostHeaders(post, authenticateWNSHeaders);
        List<NameValuePair> params = new ArrayList<>();
        for (String key : authenticateWNSPostParams.keySet()) {
            params.add(new BasicNameValuePair(key, authenticateWNSPostParams.get(key)));
        }
        post.setEntity(new UrlEncodedFormEntity(params));
        HttpResponse response = executeMethod(post);
        String conteudo = responseToString(response);

        LOG.info(conteudo);
        if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
            throw new NegocioException("Falha ao autenticar no serviço: " + conteudo);
        }

        @SuppressWarnings("unchecked")
        Map<String, String> resultMap = mapper.readValue(conteudo, HashMap.class);
        cloudMessagingDAO.setValues(resultMap);
        return oathToken = new OAuthToken(resultMap.get(KEY_ACCESS_TOKEN), resultMap.get(KEY_TOKEN_TYPE));
    }
}