Я разработал решение с использованием Spring и Apache Commons HTTP Client для отправки RAW-сообщений с помощью WNS (Windows Notification Push Service)
Вставьте эти зависимости в ваш 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>
И следующие строки в 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>
* Не забудьте заполнить client_id и client_secret
В моем проекте я разделил реализацию на четыре класса, которые я опишу ниже
OAuthToken используется для хранения и форматирования токена доступа:
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;
}
}
HttpClientFactory (показан ниже) используется для создания нового клиента при отправке сообщения. Если вы повторно используете тот же HttpClient для отправки сообщения, тайм-аут WNS после отправки второго сообщения. Я не знаю, почему это происходит, но это решилось, когда я прекратил повторное использование клиента.
@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;
}
}
Это базовый класс, вы можете выгрузить весь код в 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);
}
}
Следующий класс должен выполнить основную работу, но не забудьте создать cloudMessagingDAO
для извлечения и сохранения вашего токена доступа.
Вам следует заменить класс Jogador
на другой класс, содержащий URL-адрес клиента, который используется для отправки сообщения на устройство Windows Phone.
@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));
}
}