GCM service stops working when I implement Robospice with Spring module - google-cloud-messaging

I have implemented a standard GCM client following the instructions on google developers page (Gcm implementation). Push messages have worked great no problem there. But then I implemented Robospice REST lib in the application and GCM stopped working. I am sure it does not work because of Robospice because as soon as I remove the code for SpiceManager GCM starts working.
API calls via Robospice Spring module work fine. This is my declaration of Robospice services:
enter<receiver
android:name="com.clover.spika.enterprise.chat.services.gcm.GcmBroadcastRe ceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.clover.spika.enterprise.chat.gcm" />
</intent-filter>
</receiver>
<service android:name="com.clover.spika.enterprise.chat.services.gcm.GcmIntentService" />
<service
android:name="com.clover.spika.enterprise.chat.services.robospice.Jackson2 SpiceService"
android:exported="false" />
<service android:name="com.clover.spika.enterprise.chat.services.custom.PoolingServ ice" /> code here
This is my custom spice service:
package com.clover.spika.enterprise.chat.services.robospice;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import org.springframework.http.ContentCodingType;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import android.app.Application;
import android.app.Notification;
import com.octo.android.robospice.SpringAndroidSpiceService;
import com.octo.android.robospice.persistence.CacheManager;
import com.octo.android.robospice.persistence.exception.CacheCreationException;
import com.octo.android.robospice.persistence.springandroid.json.jackson.JacksonO bjectPersisterFactory;
public class Jackson2SpiceService extends SpringAndroidSpiceService {
// private static final int WEBSERVICES_TIMEOUT = 10000;
#Override
public RestTemplate createRestTemplate() {
RestTemplate restTemplate = new RestTemplate() {
#Override
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest request = super.createRequest(url, method);
HttpHeaders headers = request.getHeaders();
headers.setAcceptEncoding(ContentCodingType.ALL);
return request;
}
};
// bug on http connection for Android < 2.2
// http://android-developers.blogspot.fr/2011/09/androids-http-clients.html
// but still a problem for upload with Spring-android on android 4.1
// System.setProperty("http.keepAlive", "false");
// // set timeout for requests
// ClientHttpRequestFactory factory = restTemplate.getRequestFactory();
// if (factory instanceof HttpComponentsClientHttpRequestFactory) {
// HttpComponentsClientHttpRequestFactory advancedFactory =
// (HttpComponentsClientHttpRequestFactory) factory;
// advancedFactory.setConnectTimeout(WEBSERVICES_TIMEOUT);
// advancedFactory.setReadTimeout(WEBSERVICES_TIMEOUT);
// } else if (factory instanceof SimpleClientHttpRequestFactory) {
// SimpleClientHttpRequestFactory advancedFactory =
// (SimpleClientHttpRequestFactory) factory;
// advancedFactory.setConnectTimeout(WEBSERVICES_TIMEOUT);
// advancedFactory.setReadTimeout(WEBSERVICES_TIMEOUT);
// }
MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
// StringHttpMessageConverter stringHttpMessageConverter = new
// StringHttpMessageConverter();
List<MediaType> supportedMediaTypes = new ArrayList<MediaType>();
supportedMediaTypes.add(MediaType.TEXT_HTML);
supportedMediaTypes.add(MediaType.APPLICATION_JSON);
// stringHttpMessageConverter.setSupportedMediaTypes(supportedMediaTypes);
jsonConverter.setSupportedMediaTypes(supportedMediaTypes);
final List<HttpMessageConverter<?>> listHttpMessageConverters = restTemplate.getMessageConverters();
listHttpMessageConverters.add(jsonConverter);
listHttpMessageConverters.add(formHttpMessageConverter);
// listHttpMessageConverters.add(stringHttpMessageConverter);
restTemplate.setMessageConverters(listHttpMessageConverters);
return restTemplate;
}
#Override
public CacheManager createCacheManager(Application application) throws CacheCreationException {
CacheManager cacheManager = new CacheManager();
JacksonObjectPersisterFactory jacksonObjectPersisterFactory = new JacksonObjectPersisterFactory(application);
cacheManager.addPersister(jacksonObjectPersisterFactory);
return cacheManager;
}
#Override
public Notification createDefaultNotification() {
return null;
}
}
Does anyone have any idea what could be the problem? I can post additional code if it is needed.

I have solved the issue, it was not robospice or gcm fault, it was a thing in our server handling.
The thing is that we read some values from User-Agent header that i was not aware of and i did not set it to the OkHttp client.

Related

peer not authenticated - Rally javatoolkit error

I have been using the rally javatoolkit for a while to add testcases, test results etc without any error. But all of a sudden it started throwing error as
" javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated" . I have referred the pages "rally rest api java toolkit sslpeerunverifiedexception : peer not authenticated" , rally rest api java toolkit sslpeerunverifiedexception : peer not authenticated but they didn't help me. Can someone help me with what I am doing wrong. Also If i need to download a certificate please help me for windows system. Thanks in advance. my code is as below:
import com.rallydev.rest.RallyRestApi;
import com.rallydev.rest.client.HttpClient;
import com.rallydev.rest.request.GetRequest;
import com.rallydev.rest.response.GetResponse;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.scheme.Scheme;
public class ConnnectionTestWithHTTPClient {
public static void main(String[] args) throws URISyntaxException, IOException {
String host = "https://rally1.rallydev.com";
String apiKey = "_abc123";
String applicationName = "Connnection Test With HTTPClient";
RallyRestApi restApi = new RallyRestApi(new URI(host),apiKey);
restApi.setApplicationName(applicationName);
//restApi.setProxy(new URI("http://myproxy.mycompany.com"), "MyProxyUsername", "MyProxyPassword"); //SET PROXY SETTINS HERE
HttpClient client = restApi.getClient();
try {
SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy() {
public boolean isTrusted(X509Certificate[] certificate, String authType)
throws CertificateException {
//trust all certs
return true;
}
}, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
client.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, sf));
String workspaceRef = "/workspace/12345"; //USE VALID WORKSPACE OID
GetRequest getRequest = new GetRequest(workspaceRef);
GetResponse getResponse = restApi.get(getRequest);
System.out.println(getResponse.getObject());
} catch (Exception e) {
System.out.println(e);
} finally {
restApi.close();
}
}
}
Also adding to the issue, i found a different error when I changed the port from 443 to 8443. i get "java.io.IOException: HTTP/1.1 522 Origin Connection Time-out" when i use 8443.
For some reason when I uncomment the line //restApi.setProxy(new URI("http://myproxy.mycompany.com"), "MyProxyUsername", "MyProxyPassword"); with correct inputs, the error goes off.
For all those who need the inputs, please put in the following:
restApi.setProxy(new URI("http://rally1.rallydev.com"), "xyz#abc.com", "rallypassword");
so the working code is as below:
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import com.rallydev.rest.RallyRestApi;
import com.rallydev.rest.client.HttpClient;
import com.rallydev.rest.request.GetRequest;
import com.rallydev.rest.response.GetResponse;
public class ConnnectionTestWithHTTPClient {
public static void main(String[] args) throws URISyntaxException, IOException {
String host = "https://rally1.rallydev.com";
String apiKey = "_apikey";
String applicationName = "Connnection Test With HTTPClient";
RallyRestApi restApi = new RallyRestApi(new URI(host),apiKey);
restApi.setApplicationName(applicationName);
restApi.setProxy(new URI("http://rally1.rallydev.com"), "abc#abc.com", "rallypassword"); //YOUR PROXY SETTINGS HERE
HttpClient client = restApi.getClient();
try {
SSLSocketFactory sf = new SSLSocketFactory(new TrustStrategy() {
public boolean isTrusted(X509Certificate[] certificate, String authType)
throws CertificateException {
//trust all certs
return true;
}
}, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
client.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 443, sf));
String workspaceRef = "/workspace/1234";
GetRequest getRequest = new GetRequest(workspaceRef);
GetResponse getResponse = restApi.get(getRequest);
System.out.println(getResponse.getObject());
} catch (Exception e) {
System.out.println(e);
} finally {
restApi.close();
}
}
}

How to generate and manage token after spring security authentication

I have an android application where i am using spring security for authentication and authorization purpose at the server end.At first user enters the user name and password and send it via request using Rest Template to the server controller for authentication.I have done the authentication part for this user name password request.Now i have to generate a token for successive request authentication.But i don't know how to do this.Here is what I was thinking of-
a) Have an AES key generated and stored.
b) encrypt the username + timestamp with this AES key (you my have to base64 encode after this step to avoid any special characters). This will be your user token.
c) sign this token with an HMAC key (again store this in keystore).
d) send the token along with the signature (use ; as the delimiter).
e) store the token which you got in step c against the username in the DB.
The token would be send to the client as part of the response.
I don't know how to implement the above method.I have searched all over the web but didn't find anything usefull.
Below is my code that i have used for authentication.
try{
System.out.println("Request received... ");
Gson gson = new Gson();
TestUser userChk=new TestUser();
userChk=gson.fromJson(coaObj, TestUser.class);
Authentication authenticationToken = new UsernamePasswordAuthenticationToken(userChk.getUserName(), userChk.getPassword());
Authentication authentication = authenticationManager.authenticate(authenticationToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
System.out.println("getAuthorities is "+authentication.getAuthorities()+" getCredentials "+authentication.getCredentials()+" getDetails "+authentication.getDetails()+" getPrincipal "+authentication.getPrincipal());
return "Authenticated";
}catch(Exception er){
System.out.println("Authentication error "+er);
return "Authentication failure";
}
Someone please help me to generate the token in this approach and managing this.
Recently I worked on a project in which we exposed REST APIs for web and mobile apps. You can send cookies after authentication success (to maintain session). It works for mobile apps as well. You will need to extend SimpleUrlAuthenticationSuccessHandler and send cookies in response, you can also set cookie age, if you want the session to persisted for a longer time.
package com.arjun.security;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
import org.springframework.security.web.savedrequest.RequestCache;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.informage.arnav.dto.response.LoginUserResponseDto;
import com.informage.arnav.service.UserService;
import com.informage.arnav.util.ApplicationUtil;
public class AppSimpleUrlAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Autowired
ApplicationUtil applicationUtil;
#Autowired
UserService userService;
private RequestCache requestCache = new HttpSessionRequestCache();
public AppSimpleUrlAuthenticationSuccessHandler() {
super();
setRedirectStrategy(new NoRedirectStrategy());
}
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws ServletException,
IOException {
super.onAuthenticationSuccess(request, response, authentication);
AppUserDetails appUserDetails = applicationUtil.getCurrentUser();
appUserDetails.setSessionId(request.getSession().getId());
Cookie[] cookies = request.getCookies();
if (cookies.length > 0) {
for (Cookie cookie : cookies) {
if (cookie != null) {
if (cookie.getName().equals("JSESSIONID")) {
String value = cookie.getValue();
cookie.setMaxAge(1555200000);
cookie.setPath("/");
//cookie.setHttpOnly(true);
response.addCookie(cookie);
}
}
}
}
LoginUserResponseDto dto = userService.getDtoFromUserName(appUserDetails.getUsername());
Gson gson = new GsonBuilder().create();
String json = gson.toJson(dto);
response.getWriter().write(json);
response.addHeader("Content-Type", "application/json");
response.getWriter().flush();
}
}
code for getCurrentUser method is:
public AppUserDetails getCurrentUser() {
final Object principal = SecurityContextHolder.getContext()
.getAuthentication()
.getPrincipal();
final AppUserDetails appUserDetails;
if (principal != null && principal instanceof AppUserDetails) {
appUserDetails = (AppUserDetails) principal;
} else {
appUserDetails = null;
}
return appUserDetails;
}
Finally you need to configure this in your security.xml file.
<beans:property name="authenticationSuccessHandler"
ref="mySuccessHandler" />
<beans:bean id="mySuccessHandler"
class="com.informage.arnav.security.AppSimpleUrlAuthenticationSuccessHandler" />

how to get the number of busy selenium hub slots

Is there a way to get the number of busy slots on a selenium hub?
I only know the hub console url (/grid/console), there I can parse the html code ... but this is quite ugly, and quite slow if there are many slots.
Maybe somebody knows a better way?
thanks,
Alex.
This is an old question but perhaps this solution help others.
You can extend org.openqa.grid.web.servlet.RegistryBasedServlet, inject it to the hub and get information via http/json
This is the code for the class:
import java.io.IOException;
import java.util.Iterator;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.openqa.grid.common.exception.GridException;
import org.openqa.grid.internal.ProxySet;
import org.openqa.grid.internal.Registry;
import org.openqa.grid.internal.RemoteProxy;
import org.openqa.grid.internal.TestSlot;
import org.openqa.grid.web.servlet.RegistryBasedServlet;
import org.openqa.selenium.Platform;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import com.google.gson.JsonArray;
import com.google.gson.JsonIOException;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
public class NodesInfoServlet extends RegistryBasedServlet {
private static final long serialVersionUID = -5559403361498232207L;
public NodesInfoServlet() {
super(null);
}
public NodesInfoServlet(Registry registry) {
super(registry);
}
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
process(req, resp);
}
protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/json");
response.setCharacterEncoding("UTF-8");
response.setStatus(200);
JsonObject res;
try {
res = getFreeBrowsers();
response.getWriter().print(res);
response.getWriter().close();
} catch (JsonIOException e) {
throw new GridException(e.getMessage());
}
}
private JsonObject getFreeBrowsers() throws IOException, JsonIOException {
JsonObject requestJSON = new JsonObject();
ProxySet proxies = this.getRegistry().getAllProxies();
JsonObject platformsbrowsers = new JsonObject();
for (RemoteProxy proxy : proxies) {
for (TestSlot slot : proxy.getTestSlots()) {
if (slot.getSession() == null){
// Plataforma del slot
Platform ptf = getPlatform(slot);
if (ptf != null){
JsonArray browserlist = new JsonArray();
if (!platformsbrowsers.has(ptf.toString())){
platformsbrowsers.add(ptf.toString(), browserlist);
}
JsonArray platform = platformsbrowsers.getAsJsonArray(ptf.toString());
// Browser del slot
DesiredCapabilities cap = new DesiredCapabilities(slot.getCapabilities());
JsonPrimitive browser = new JsonPrimitive(cap.getBrowserName());
if (!platform.contains(browser)){
platform.add(browser);
}
}
}
}
}
requestJSON.add("PlatformsBrowsers", platformsbrowsers);
return requestJSON;
}
private static Platform getPlatform(TestSlot slot) {
Object o = slot.getCapabilities().get(CapabilityType.PLATFORM);
if (o == null) {
return Platform.ANY;
} else {
if (o instanceof String) {
return Platform.valueOf((String) o);
} else if (o instanceof Platform) {
return (Platform) o;
} else {
throw new GridException("Cannot cast " + o + " to org.openqa.selenium.Platform");
}
}
}
}
You need to inject in the hub:
java -classpath C:\Selenium\selenium-server-standalone.jar;C:\Selenium\NodesInfoServlet.jar; org.openqa.grid.selenium.GridLauncher -role hub -hubConfig hubConfig.json -servlets NodesInfoServlet
You get the results via http:
http://localhost:4444/grid/admin/NodesInfoServlet
Result:
{
PlatformsBrowsers: {
VISTA: [
"internet explorer",
"phantomjs",
"htmlunit"
]
}
}
Hope this helps!!!
What I researched so far, the Selenium API is not providing a method to get the hub information from the client side. It is pretty ugly to parse the source code but that is working for me. Other people mentioned to make some changes to the selenium hub jar file but that won't be scalable and it will be needed with each selenium version. (Firefox versions and Selenium versions are all the time changing... I wouldn't recommend you to do that)
If you are using Java, you can try to use Json simple to parse the string and get easily the browsers.
https://code.google.com/p/json-simple/
I hope this can be helpful.

Not able to receive GCM message in my device, even though the server got success message

I got success message as response to web server (localhost) from gcm. But the message is not received by the devices. Please help. Below is the code which I used.
Main Activity
package vitpat.placement;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.gcm.GoogleCloudMessaging;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.StrictMode;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends Activity {
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
public static final String EXTRA_MESSAGE = "message";
public static final String PROPERTY_REG_ID = "registration_id";
private static final String PROPERTY_APP_VERSION = "appVersion";
TextView textUnderProgressBar;
String SENDER_ID = "794097343372";
final String TAG = "GCMDemo";
TextView mDisplay;
GoogleCloudMessaging gcm;
AtomicInteger msgId = new AtomicInteger();
SharedPreferences prefs;
Context context;
String regid;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (android.os.Build.VERSION.SDK_INT > 9) {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
textUnderProgressBar = (TextView) findViewById(R.id.textView1);
context = getApplicationContext();
//shared preference to check if the application is being run for the first time and the user is yet to be verified
SharedPreferences prefToVerifyUser = getApplicationContext().getSharedPreferences("MyPref", 0); // 0 - for private mode
Boolean isUserVerified = prefToVerifyUser.getBoolean("UserVerified", false);
Log.e("main activity", isUserVerified.toString());
if(isUserVerified == false) {
Intent intent = new Intent(this,Check_User_Authentication.class);
startActivityForResult(intent, 0);
}
else {
//end of shared preference check
ProgressBar progress = (ProgressBar) findViewById(R.id.progressBar1);
progress.setIndeterminate(true);
progress.setVisibility(View.VISIBLE);
// Check device for Play Services APK.
if(CheckForPlayStoreApp()) {
//code
textUnderProgressBar.setText("Contacting Server..");
gcm = GoogleCloudMessaging.getInstance(this);
regid = getRegistrationId(context);
if (regid.isEmpty()) {
registerInBackground();
}
else {
textUnderProgressBar.setText(regid.toString());
/*GcmBroadcastReceiver gcr;
GcmBroadcastReceiver.startWakefulService(getApplicationContext(), getIntent());*/
}
} else {
Log.i(TAG, "No valid Google Play Services APK found.");
}
}
}
/**
* Check the device to make sure it has the Google Play Services APK. If
* it doesn't, display a dialog that allows users to download the APK from
* the Google Play Store or enable it in the device's system settings.
*/
private boolean CheckForPlayStoreApp() {
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
GooglePlayServicesUtil.getErrorDialog(resultCode, this,
PLAY_SERVICES_RESOLUTION_REQUEST).show();
} else {
Log.i("CheckForPlayStoreApp", "This device is not supported.");
finish();
}
return true;
}
return true;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
// You need to do the Play Services APK check here too.
#Override
protected void onResume() {
super.onResume();
CheckForPlayStoreApp();
}
/**
* Gets the current registration ID for application on GCM service.
* <p>
* If result is empty, the app needs to register.
*
* #return registration ID, or empty string if there is no existing
* registration ID.
*/
private String getRegistrationId(Context context) {
final SharedPreferences prefs = getGCMPreferences(context);
String registrationId = prefs.getString(PROPERTY_REG_ID, "");
if (registrationId.isEmpty()) {
Log.i(TAG, "Registration not found.");
return "";
}
// Check if app was updated; if so, it must clear the registration ID
// since the existing regID is not guaranteed to work with the new
// app version.
int registeredVersion = prefs.getInt(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
int currentVersion = getAppVersion(context);
if (registeredVersion != currentVersion) {
Log.i(TAG, "App version changed.");
return "";
}
return registrationId;
}
/**
* #return Application's {#code SharedPreferences}.
*/
private SharedPreferences getGCMPreferences(Context context) {
// This sample app persists the registration ID in shared preferences, but
// how you store the regID in your app is up to you.
return getSharedPreferences(MainActivity.class.getSimpleName(),
Context.MODE_PRIVATE);
}
/**
* #return Application's version code from the {#code PackageManager}.
*/
private static int getAppVersion(Context context) {
try {
PackageInfo packageInfo = context.getPackageManager()
.getPackageInfo(context.getPackageName(), 0);
return packageInfo.versionCode;
} catch (NameNotFoundException e) {
// should never happen
throw new RuntimeException("Could not get package name: " + e);
}
}
/**
* Registers the application with GCM servers asynchronously.
* <p>
* Stores the registration ID and app versionCode in the application's
* shared preferences.
*/
private void registerInBackground() {
Log.e("inside register", "sample0");
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
Log.e("inside register", "sample1");
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
Log.e("inside register", regid);
msg = "Device registered, registration ID=" + regid;
// You should send the registration ID to your server over HTTP,
// so it can use GCM/HTTP or CCS to send messages to your app.
// The request to your server should be authenticated if your app
// is using accounts.
sendRegistrationIdToBackend();
// For this demo: we don't need to send it because the device
// will send upstream messages to a server that echo back the
// message using the 'from' address in the message.
// Persist the regID - no need to register again.
storeRegistrationId(context, regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
// If there is an error, don't just keep trying to register.
// Require the user to click a button again, or perform
// exponential back-off.
}
return null;
}
}.execute(null, null, null);
}
/**
* Sends the registration ID to your server over HTTP, so it can use GCM/HTTP
* or CCS to send messages to your app. Not needed for this demo since the
* device sends upstream messages to a server that echoes back the message
* using the 'from' address in the message.
*/
private void sendRegistrationIdToBackend() {
// Your implementation here.
String url = "http://192.168.43.112/dist/addregistrationid.php";
SharedPreferences prefUserDetails = getApplicationContext().getSharedPreferences("UserDetails", 0);
String registerNumber = prefUserDetails.getString("registerNumber", null);
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("regid", regid));
params.add(new BasicNameValuePair("regno", registerNumber));
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(url);
try {
httpPost.setEntity(new UrlEncodedFormEntity(params));
}
catch (UnsupportedEncodingException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
try {
HttpResponse httpResponse = httpClient.execute(httpPost);
} catch (ClientProtocolException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* Stores the registration ID and app versionCode in the application's
* {#code SharedPreferences}.
*
* #param context application's context.
* #param regId registration ID
*/
private void storeRegistrationId(Context context, String regId) {
final SharedPreferences prefs = getGCMPreferences(context);
int appVersion = getAppVersion(context);
Log.i(TAG, "Saving regId on app version " + appVersion);
SharedPreferences.Editor editor = prefs.edit();
editor.putString(PROPERTY_REG_ID, regId);
editor.putInt(PROPERTY_APP_VERSION, appVersion);
editor.commit();
}
}
GcmBroadcastReceiver
public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
ComponentName comp = new ComponentName(context.getPackageName(),GcmIntentService.class.getName());
// Start the service, keeping the device awake while it is launching.
startWakefulService(context, (intent.setComponent(comp)));
setResultCode(Activity.RESULT_OK);
}
}
GcmIntentService
public class GcmIntentService extends IntentService {
Context context;
public static final int NOTIFICATION_ID = 1;
private NotificationManager mNotificationManager;
NotificationCompat.Builder builder;
public static final String TAG = "GCM Demo";
public GcmIntentService() {
super("GcmIntentService");
// TODO Auto-generated constructor stub
}
#Override
protected void onHandleIntent(Intent intent) {
// TODO Auto-generated method stub
Bundle extras = intent.getExtras();
String msg = intent.getStringExtra("message");
GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
String messageType = gcm.getMessageType(intent);
if (!extras.isEmpty()) {
if (GoogleCloudMessaging.
MESSAGE_TYPE_SEND_ERROR.equals(messageType)) {
sendNotification("Send error: " + extras.toString());
} else if (GoogleCloudMessaging.
MESSAGE_TYPE_DELETED.equals(messageType)) {
sendNotification("Deleted messages on server: " +
extras.toString());
// If it's a regular GCM message, do some work.
} else if (GoogleCloudMessaging.
MESSAGE_TYPE_MESSAGE.equals(messageType)) {
// This loop represents the service doing some work.
for (int i=0; i<5; i++) {
Log.i(TAG, "Working... " + (i+1)
+ "/5 # " + SystemClock.elapsedRealtime());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
}
}
Log.i(TAG, "Completed work # " + SystemClock.elapsedRealtime());
// Post notification of received message.
//sendNotification("Received: " + extras.toString());
sendNotification(msg);
Log.i(TAG, "Received: " + extras.toString());
}
}
GcmBroadcastReceiver.completeWakefulIntent(intent);
}
private void sendNotification(String msg) {
mNotificationManager = (NotificationManager)
this.getSystemService(Context.NOTIFICATION_SERVICE);
Intent myintent = new Intent(this, ReceiveActivity.class);
myintent.putExtra("message", msg);
PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
myintent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
// .setSmallIcon(R.drawable.ic_stat_gcm)
.setContentTitle("GCM Notification")
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(msg))
.setContentText(msg);
mBuilder.setContentIntent(contentIntent);
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
}
}
Manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="vitpat.placement"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="11"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<permission android:name="vitpat.placement.permission.C2D_MESSAGE"
android:protectionLevel="signature" />
<uses-permission android:name="vitpat.placement.permission.C2D_MESSAGE" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="vitpat.placement.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="vitpat.placement.Check_User_Authentication"
android:theme="#android:style/Theme.Dialog">
</activity>
<receiver
android:name=".GcmBroadcastReceiver"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="vitpat.placement" />
</intent-filter>
</receiver>
<service android:name=".GcmIntentService" />
<meta-data android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />
<activity android:name="vitpat.placement.ReceiveActivity"></activity>
</application>
</manifest>
I found the solution myself. I was using the "android api key" to send the message from my web server (php) to gcm server and that was the problem. Using "browser api" key solved the issue..
Create a new "browser api key" from google console and use it in the php code to send a message from web server to gcm.
If you want to send a message from android device to gcm, try using android key.
Make sure, you create all these keys from the same project under google console.
I had same problem of receiving push messages, even though GCM server replies "Success", because of wifi privacy. When I used my company wifi, I coudn't receive message in mobile, since they were blocked GCM server port . So, even your wifi also could be a one of the reason. check it once { this answer is for others , who may face this}

Safari Push Notifications

I would like to implement push notifications for my website (obviously only in compatible browser as Safari 7).
I have read the Apple documentation and I have successfully created my package containing my icon.iconset, my certificate.p12, manifest.json and a website.json.
Now I would like to ask the permission to the user when I first visit the website. If he allows it, I should send the package.
Everything is pretty clear but I don't know how to go on.
How do I create my push package out of my files? How do I precisely sign it? The package should be always the same so I could sign it on my mac and upload to my server only one package.
If you have experience with this technology, please let me know :)
I have successfully create push package to ask permission on safari web push notifications using REST API in java. Also I have follow steps which provide by Apple officials on there sites.
Please follow below steps to create push package.
Create your web push notification P12 certificate from your apple account.
Create your REST API using https for pushPackage which contain icon.iconset, your certificate.p12, manifest.json and a website.json. p12 certificate must be web push notification certificate.
How to create push package:- Please refer below java code. I have create push package using java servlet. which provide 2 web service end points.
v1/pushpackage/webpushID
v1/log
Servlet which process your push package request which send by safari push notification method
SafariPushPakageAPI.java
/*
Push Package REST API handler
*/
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import com.safari.Packager;
public class SafariPushPakageAPI extends HttpServlet{
/**
*
*/
private static final long serialVersionUID = 1L;
public static String ServerPath = null;
private static final String REQUEST_PERMISSION = "/v1/pushPackages/YOUR_WEB_PUSH_ID";
private static final String REQUEST_ERRORLOG = "/v1/log";
#Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doRequest(request, response);
}
// /v1/pushPackages/webpushID
// /v1/log
#Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doRequest(request, response);
}
private void doRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("===>> SAFARI PUSH NOTIFICATION REQUEST");
String path = request.getPathInfo();
System.out.println("PATH ===>> "+path);
if(path == null){
doRequestPermission(request, response);
}else if (path.equalsIgnoreCase(REQUEST_PERMISSION)){
doRequestPermission(request, response);
}else if (path.equalsIgnoreCase(REQUEST_ERRORLOG)){
doRequestShowErrorLog(request, response);
}else{
doRequestPermission(request, response);
}
}
private void doRequestPermission(HttpServletRequest request,HttpServletResponse response) {
try{
System.out.println("INSIDE REQUEST PERMISSION ==>>>");
System.out.println(IOUtils.toString(request.getReader()));
String authToken = StringUtils.isBlank(request.getParameter("token")) ? "UserTokenRT124DFGH" : StringUtils.trimToEmpty(request.getParameter("token"));
System.out.println("=>>>>>>>>>> USER TOKEN =>>>>>>>>>> "+authToken);
#SuppressWarnings("deprecation")
String packagePath =request.getRealPath("pushPackage.raw/icon.iconset/"); // LOCATION WHERE YOUR PUSH PACKAGE FOLDER CONTAIN LOGOS AND website.json file
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment;filename=\"pushpackage.zip\"");
OutputStream out = response.getOutputStream();
out.write(Packager.createPackageFile(authToken,packagePath));
response.flushBuffer();
}catch(IOException ioe){
ioe.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
private void doRequestShowErrorLog(HttpServletRequest request,HttpServletResponse response) {
try{
System.out.println("ERROR LOG STARTED");
System.out.println(IOUtils.toString(request.getReader()));
System.out.println("END");
}catch(Exception e){
e.printStackTrace();
}
}
}
Packager.java
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import org.json.JSONArray;
import org.json.JSONObject;
/**
*
* #author Ritesh
*/
public class Packager {
final static String CERTIFICATE_PATH="PATH TO YOUR 12 CERTIFICATE";
final static String CERTIFICATE_PASS="PASSWORD";
static String getJSON(String authenticationToken) throws Exception {
JSONObject obj = new JSONObject();
obj.put("websiteName", "WEB SITE NAME");
obj.put("websitePushID", "WEB PUSH ID");
obj.put("allowedDomains", new JSONArray());
obj.getJSONArray("allowedDomains").put("https://TEST.EXAMPLE.net");//LIST OF DOMAINS ALLOW
obj.put("urlFormatString", "https://TEST.EXAMPLE.net/%#");
obj.put("authenticationToken", authenticationToken);
obj.put("webServiceURL", "https://API.EXAMPLE.COM");//callback URL WITHOUT WEB SERVICE ENDPOINT NAME
return obj.toString();
}
public static byte[] createPackageFile(String authenticationToken, String path) throws Exception {
System.out.println("packaging safari file with token: " + authenticationToken);
ZipHandler zip = new ZipHandler();
File dir = new File(path);
for (File file : dir.listFiles()) {
InputStream is = new FileInputStream(file);
byte[] bytes = IOUtils.toByteArray(is);
zip.addFile("icon.iconset", file.getName(),bytes );
}
zip.addFile("", "website.json", getJSON(authenticationToken).getBytes());
byte[] manifest = zip.manifest();
zip.addFile("", "manifest.json", manifest);
zip.addFile("", "signature", sign(manifest));
return zip.getBytes();
}
static byte[] sign(byte bytesToSign[]) throws Exception {
return new PKCS7Signer().sign(CERTIFICATE_PATH,CERTIFICATE_PASS, bytesToSign);
}
/**
* Servlet handler , should listen on the callback URL (as in webServiceURL)
* #param requestPath
* #param req
* #param servletRequest
* #param servletResponse
* #throws Exception
*/
public static void main(String[] args) throws Exception {
Packager.createPackageFile("SafriNotifcation","");
}
}
PKCS7Signer.java which create your signature file.
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.CMSTypedData;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
public final class PKCS7Signer {
static {
try{
Security.addProvider(new BouncyCastleProvider());
}catch(Exception e){
e.printStackTrace();
}
}
private KeyStore getKeystore(String storeLocation, String storePasswd) throws Exception {
if (storeLocation == null) {
System.out.println("Could not find store file (.p12)");
return null;
}
// First load the keystore object by providing the p12 file path
KeyStore clientStore = KeyStore.getInstance("PKCS12");
// replace testPass with the p12 password/pin
clientStore.load(new FileInputStream(storeLocation), storePasswd.toCharArray());
return clientStore;
}
private X509CertificateHolder getCert(KeyStore keystore, String alias) throws Exception {
java.security.cert.Certificate c = keystore.getCertificate(alias);
return new X509CertificateHolder(c.getEncoded());
}
private PrivateKey getPrivateKey(KeyStore keystore, String alias, String storePasswd) throws Exception {
return (PrivateKey) keystore.getKey(alias, storePasswd.toCharArray());
}
public byte[] sign(String storeLocation, String storePasswd, byte[] dataToSign) throws Exception {
KeyStore clientStore = getKeystore(storeLocation, storePasswd);
if (clientStore == null) {
return null;
}
Enumeration aliases = clientStore.aliases();
String alias = "";
while (aliases.hasMoreElements()) {
alias = (String) aliases.nextElement();
if (clientStore.isKeyEntry(alias)) {
break;
}
}
CMSTypedData msg = new CMSProcessableByteArray(dataToSign); // Data to sign
X509CertificateHolder x509Certificate = getCert(clientStore, alias);
List certList = new ArrayList();
certList.add(x509Certificate); // Adding the X509 Certificate
Store certs = new JcaCertStore(certList);
CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
// Initializing the the BC's Signer
ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC").build(
getPrivateKey(clientStore, alias, storePasswd));
gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder()
.setProvider("BC").build()).build(sha1Signer, x509Certificate));
// adding the certificate
gen.addCertificates(certs);
// Getting the signed data
CMSSignedData sigData = gen.generate(msg, false);
return sigData.getEncoded();
}
}
please note used latest bouncy castle jars for create signature file
bcprov-jdk15on-157.jar
bcpkix-jdk15on-157.jar
Write your javascript on your client side page.it contains simple java script to get permissions.
///
// For safari
var domain="YOUR WEB PUSH ID";
function safariIniti() {
var pResult = window.safari.pushNotification.permission(domain);
if(pResult.permission === 'default') {
//request permission
requestPermissions();
} else if (pResult.permission === 'granted') {
console.log("Permission for " + domain + " is " + pResult.permission);
var token = pResult.deviceToken;
// Show subscription for debug
console.log('Subscription details:'+token);
} else if(pResult.permission === 'denied') {
console.log("Permission for " + domain + " is " + pResult.permission);
}
}
function getToken(){
// always start with a letter (for DOM friendlyness)
var idstr=String.fromCharCode(Math.floor((Math.random()*25)+65));
do {
// between numbers and characters (48 is 0 and 90 is Z (42-48 = 90)
var ascicode=Math.floor((Math.random()*42)+48);
if (ascicode<58 || ascicode>64){
// exclude all chars between : (58) and # (64)
idstr+=String.fromCharCode(ascicode);
}
} while (idstr.length<32);
return (idstr);
}
function requestPermissions() {
var tokenVal = getToken();
window.safari.pushNotification.requestPermission('WEb service url without end points',domain,{token:tokenVal},
function(subscription) {
console.log(subscription.permission);
console.log("PERMISSION ====>> "+subscription.permission);
if(subscription.permission === 'granted') {
//TODO
}
else if(subscription.permission === 'denied') {
// TODO:
}
});
}
Apple provides a php file that you can use to create your push package including the signature. https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NotificationProgrammingGuideForWebsites/CompanionFile.zip
Alternatively, you can use the push_package gem, https://github.com/SymmetricInfinity/push_package, which we developed when implementing safari push notifications for zeropush.com. More details are available at https://zeropush.com/blog/implementing-safari-push-notifications-in-osx-mavericks.
Follow this apple documentation and github repo, they include sufficient information required to create a safari push notifications.