I have a class that controls my http communications, as well as keeps track of the http session. The problem i'm running into is, i would like to use this class from several activities.
The only option i have found so far is making it static in the main view, but if the app sits to long in a secondary view, the static class becomes null.
I have also tried making it parcelable, but i can't pass my CookieStore or my httpContext. This means every time I go into a view, or out of a view, i have to re-authenticate against the server, causing much unnecessary traffic.
How can i pass an object via Parcelable, or is there another type of class extension that would allow me to create a persistent class that all Activity's can view?
public class JSON implements Parcelable {
private String URL;
private String HOSTNAME;
private Context f_context;
private DefaultHttpClient httpClient;
private CookieStore cookieStore;
private HttpContext httpContext;
protected boolean Authenticated = false;
protected long Authenticate_timeout = 0;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
Bundle b = new Bundle();
b.putString("URL",URL);
b.putString("HOSTNAME", HOSTNAME);
b.putBoolean("Authenticated", Authenticated);
b.putLong("Authenticate_timeout", Authenticate_timeout);
out.writeBundle(b);
Object[] o = new Object[4];
o[0] = httpClient;
o[1] = cookieStore;
o[2] = httpContext;
o[3] = f_context;
out.writeArray(o);
}
public static final Parcelable.Creator<JSON> CREATOR = new Parcelable.Creator<JSON>() {
public JSON createFromParcel(Parcel in) {
return new JSON(in);
}
public JSON[] newArray(int size) {
return new JSON[size];
}
};
private JSON(Parcel in) {
Object[] o = in.readArray(null);
httpClient = (DefaultHttpClient) o[0];
cookieStore = (CookieStore) o[1];
httpContext = (HttpContext) o[2];
f_context = (Context) o[3];
Bundle b = in.readBundle();
URL = b.getString("URL");
HOSTNAME = b.getString("HOSTNAME");
Authenticated = b.getBoolean("Authenticated");
Authenticate_timeout = b.getLong("Authenticate_timeout");
}
public JSON(Context context) {
f_context = context;
updateSettings();
HttpParams myParams = new BasicHttpParams();
HttpConnectionParams
.setConnectionTimeout(myParams, Consts.http_timeout);
HttpConnectionParams.setSoTimeout(myParams, Consts.http_timeout);
httpClient = new DefaultHttpClient(myParams);
cookieStore = new BasicCookieStore();
httpContext = new BasicHttpContext();
httpContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
}
Related
I run a custom WebSocketServlet for Jetty, which sends short text push notifications (for an async mobile and desktop word game) to many platforms (Facebook, Vk.com, Mail.ru, Ok.ru also Firebase and Amazon messaging) using a Jetty HttpClient instance:
public class MyServlet extends WebSocketServlet {
private final SslContextFactory mSslFactory = new SslContextFactory();
private final HttpClient mHttpClient = new HttpClient(mSslFactory);
#Override
public void init() throws ServletException {
super.init();
try {
mHttpClient.start();
} catch (Exception ex) {
throw new ServletException(ex);
}
mFcm = new Fcm(mHttpClient); // Firebase
mAdm = new Adm(mHttpClient); // Amazon
mApns = new Apns(mHttpClient); // Apple
mFacebook = new Facebook(mHttpClient);
mMailru = new Mailru(mHttpClient);
mOk = new Ok(mHttpClient);
mVk = new Vk(mHttpClient);
}
This has worked very good for the past year, but since I have recently upgraded my WAR-file to use Jetty 9.4.14.v20181114 the trouble has begun -
public class Facebook {
private final static String APP_ID = "XXXXX";
private final static String APP_SECRET = "XXXXX";
private final static String MESSAGE_URL = "https://graph.facebook.com/%s/notifications?" +
// the app access token is: "app id | app secret"
"access_token=%s%%7C%s" +
"&template=%s";
private final HttpClient mHttpClient;
public Facebook(HttpClient httpClient) {
mHttpClient = httpClient;
}
private final BufferingResponseListener mMessageListener = new BufferingResponseListener() {
#Override
public void onComplete(Result result) {
if (!result.isSucceeded()) {
LOG.warn("facebook failure: {}", result.getFailure());
return;
}
try {
// THE jsonStr SUDDENLY CONTAINS PREVIOUS CONTENT!
String jsonStr = getContentAsString(StandardCharsets.UTF_8);
LOG.info("facebook success: {}", jsonStr);
} catch (Exception ex) {
LOG.warn("facebook exception: ", ex);
}
}
};
public void postMessage(int uid, String sid, String body) {
String url = String.format(MESSAGE_URL, sid, APP_ID, APP_SECRET, UrlEncoded.encodeString(body));
mHttpClient.POST(url).send(mMessageListener);
}
}
Suddenly the getContentAsString method called for successful HttpClient invocations started to deliver the strings, which were fetched previously - prepended to the the actual result string.
What could it be please, is it some changed BufferingResponseListener behaviour or maybe some non-obvious Java quirk?
BufferingResponseListener was never intended to be reusable across requests.
Just allocate a new BufferingResponseListener for every request/response.
Why wont this compile:
MyOkHttpClient okClient = new MyOkHttpClient.Builder()
.addInterceptor(new AddCookiesInterceptor())
.addInterceptor(new ReceivedCookiesInterceptor()).build();
Incompatible types.
Required:
my.path.util.auth.MyOkHttpClient
Found:
okhttp3.OkHttpClient
This is MY class:
public class MyOkHttpClient extends okhttp3.OkHttpClient implements Authenticator {
private static int MAX_AUTHENTICATE_TRIES = 3;
#Override
public Request authenticate(Route route, Response response) throws IOException {
if (responseCount(response) >= MAX_AUTHENTICATE_TRIES) {
return null; // If we've failed 3 times, give up. - in real life, never give up!!
}
String credential = Credentials.basic(AUTHTOKEN_USERNAME, AUTHTOKEN_PASSWORD);
return response.request().newBuilder().header("Authorization", credential).build();
}
private int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
}
Based on your comments, you incorrectly believe that you are decorating OkHttpClient with custom authentication logic.
Instead, you are unnecessarily extending OkHttpClient and implementing the Authenticator interface. You can simply build the standard OkHttpClient with any custom authenticator you would like.
As such, this is more like what you actually want
public class MyAuthenticator implements Authenticator {
private static int MAX_AUTHENTICATE_TRIES = 3;
#Override
public Request authenticate(Route route, Response response) throws IOException {
if (responseCount(response) >= MAX_AUTHENTICATE_TRIES) {
return null; // If we've failed 3 times, give up. - in real life, never give up!!
}
String credential = Credentials.basic(AUTHTOKEN_USERNAME, AUTHTOKEN_PASSWORD);
return response.request().newBuilder().header("Authorization", credential).build();
}
private int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
}
And then when you build your client
OkHttpClient okClient = new OkHttpClient.Builder()
.addInterceptor(new AddCookiesInterceptor())
.addInterceptor(new ReceivedCookiesInterceptor())
.authenticator(new MyAuthenticator())
.build();
I have an asyncTask in my application as below. I have to fetch data from realm(which is successfully stored in another activity) into this AsyncTask. Below is my AsyncTask code:
public class MakeAsyncRequest extends AsyncTask<Object, String, MakeAsyncRequest.ResponseDataType>{
public Context asyncContext;
private MakeAsyncRequest(Context context){
asyncContext = context;
}
private static final OkHttpClient client = new OkHttpClient();
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private MakeRequest.ResponseHandler handler;
private String method;
private User user;
private Realm realm;
class ResponseDataType
{
InputStream inputStream;
String string;
String cookie;
}
MakeAsyncRequest(MakeRequest.ResponseHandler responseHandler, String type)
{
method = type;
handler = responseHandler;
}
#Override
protected ResponseDataType doInBackground(Object... params) {
try {
Requests requests = new Requests((Context) params[0]);
String url = params[1].toString();
String bodyJson = null;
if(method.equals("PUT") || method.equals("POST")) {
bodyJson = params[2].toString();
}
final Request.Builder builder;
Response response;
RequestBody body;
switch (method) {
case "GET": builder = new Request.Builder().url(url);
break;
case "DOWNLOAD": builder = new Request.Builder().url(url);
break;
case "POST": body = RequestBody.create(JSON, bodyJson);
builder = new Request.Builder()
.url(url)
.post(body)
.addHeader("Cookie", "key=value");
break;
case "PUT": body = RequestBody.create(JSON, bodyJson);
builder = new Request.Builder()
.url(url)
.put(body);
break;
default: builder = new Request.Builder().url(url);
}
builder.addHeader("Accept", "application/json");
realm = RealmController.initialize(this).getRealm();
final RealmResults<User> users = realm.where(User.class).findAllAsync();
user = users.first();
if(user.getCookie() !== null && !user.getCookie().isEmpty()){
builder.addHeader("cookie", "user.getCookie()");
}
response = client.newCall(builder.build()).execute();
ResponseDataType responseDataType = new ResponseDataType();
if(method.equals("DOWNLOAD")) { responseDataType.inputStream = response.body().byteStream(); }
else {
responseDataType.string = response.body().string();
responseDataType.cookie = response.headers().get("set-cookie");
CookieManager cManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
cManager.getCookieStore().getCookies();
}
return responseDataType;
} catch (IOException e) {
return null;
}
}
#Override
protected void onPostExecute(ResponseDataType response) {
try {
if (method.equals("DOWNLOAD")) {
handler.onFinishCallback(response.inputStream);
}else {
handler.onFinishCallback(response.string, response.cookie);
CookieManager cManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
cManager.getCookieStore().getCookies();
}
}catch (Exception e){
Log.d("hExceptionError", e.toString());
handler.onFinishCallback("{\n" +
" \"error\": \"error\"\n" +
"}","");
}
}
I am received the access error - "Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created." whenever the control tries to execute the Realm results or to get the first object from Realm.
Below is my RealmController which i created to control the realm instance:
public class RealmController {`public static RealmController initialize(Activity activity) {
if (instance == null) {
instance = new RealmController(activity.getApplication());
}
return instance;
}
public static RealmController initialize(MakeAsyncRequest activity) {
if (instance == null) {
instance = new RealmController(activity.);
}
return instance;
}
}`
I have my user model(Realm object) with setters and getters.
The point is - you cannot create and access Realm on different threads, i.e. create Realm instance in Activity and use it in .doInBackground() method. Create and release Realm immediately before and after transaction.
There may be another issue - don't register quer observer on background thread in AsyncTask - it doesn't have Looper initialized - use main thread or HandlerThread.
Release realm after it is no longer needed (you didn't in your code), because Realm has limited number of instances.
You can initiate a Realm instance in the doInBackground method of your AsyncTask like this:
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
Realm realm = Realm.getDefaultInstance();
// Do your Realm work here
realm.close();
return null;
}
}
It's important to note that you should always open and close a Realm instance in the same thread. In this case, since the AsyncTask is running in a background thread, you can open and close the Realm instance within the doInBackground method.
I have been working in a simple SNMP manager for java using the library SNMP4J. I am trying to send simple asynchronous get messages. I have greated a SNMPmanager to create the PDUs and start the listeners and a simple class named SNMPget that makes a simple get request.
As stated in the javadoc:
Asynchronous responses are returned by calling a callback method on an object instance that implements the ResponseListener interface.
I have followed the exact instructions to implement the callback but looks like the callback method onResponse is never executed when sending a get request. In fact, I have used wireshark to check if packets are being send and the request is correctly send and the correct response is received. Any clue on what I am doing wrong?
public class SNMPManager {
private static SNMPManager instance = new SNMPManager();
private Snmp snmp_ = null;
//Common SNMP variables
private int requestPort = 161;
private int timeout = 10000;
private int retryCount = 1;
private int maxRepetitions = 20;
private int nonRepeaters = 0;
//Singleton pattern
public static SNMPManager getInstance() {
return instance;
}
//Empty Constructor
private SNMPManager() { }
//Start the manager
public void start() throws IOException {
//Create a UDP transport on all interfaces
DefaultUdpTransportMapping transport = new DefaultUdpTransportMapping();
//Create a SNMP instance
snmp_ = new Snmp(transport);
//Put all the transport mappings into listen mode
snmp_.listen();
}
//Methods for creating targets
// Create the target for version 2c
public Target createVersion2cTarget(String ipaddress, String communityName) {
CommunityTarget target = new CommunityTarget();
target.setAddress(new UdpAddress(String.format("%s/%d", ipaddress, requestPort)));
target.setCommunity(new OctetString(communityName));
target.setTimeout(timeout);
target.setRetries(retryCount);
target.setVersion(SnmpConstants.version2c);
return target;
}
//Methods for creating PDUs
public PDU createGetRequestPdu(String requestOID) {
PDU pdu = new PDU();
//Set the request type
pdu.setType(PDU.GET);
//Set the OID
OID rOID = new OID(requestOID);
pdu.add(new VariableBinding(rOID));
return pdu;
}
//Methods for the request (async mode)
public void getRequest(PDU pdu, Target target, Object userHandle, ResponseListener listener) throws IOException {
snmp_.get(pdu, target, userHandle, listener);
}
}
public class SNMPget {
protected final SNMPManager snmp_;
private String requestOID;
private String ipAddress;
private String communityName;
public SNMPget(String newRequestOID, String hostIpAddress, String newCommunityName) {
snmp_ = SNMPManager.getInstance();
requestOID = newRequestOID;
ipAddress = hostIpAddress;
communityName = newCommunityName;
}
public void get(ResponseListener listener) throws IOException {
Target targetHost = snmp_.createVersion2cTarget(ipAddress, communityName);
PDU pduSNMPget = snmp_.createGetRequestPdu(requestOID);
snmp_.getRequest(pduSNMPget, targetHost, null, listener);
}
}
public class mainTest {
public static void main(String[] args) throws IOException {
SNMPManager snmp = SNMPManager.getInstance();
snmp.start();
ResponseListener listener = new ResponseListener() {
#Override
public void onResponse(ResponseEvent event) {
// Always cancel async request when response has been received
// otherwise a memory leak is created! Not canceling a request
// immediately can be useful when sending a request to a broadcast
// address.
((Snmp) event.getSource()).cancel(event.getRequest(), this);
PDU response = event.getResponse();
System.out.println("Received response "+response);
}
};
SNMPget snmpGetCmd = new SNMPget("1.3.6.1.2.1.1.1.0", "192.168.137.104", "public");
snmpGetCmd.get(listener);
}
}
I have an AsyncTask which calls a webservice method and sends the returned data back to the main thread. This task does not affect any UI elements yet throws CalledFromWrongThreadException on the line calling onResultSuccess. I have many other AsyncTask which work in basically the exact same way, changing the called web service method and parameters, etc, but this is the only one that fails. Anyone spot what might be the problem? The AsyncTask looks like:
public static class ClockOff extends AsyncTask<String, Void, Void>
{
public OnAsyncResultClockOff onAsyncResultClockOff;
public void setOnResultListener(OnAsyncResultClockOff onAsyncResultClockOff) {
if(onAsyncResultClockOff != null) {
ResultClockOff = onAsyncResultClockOff;
}
}
private static final String SOAP_ACTION = "http://tempuri.org/ClockOff";
private static final String METHOD_NAME = "ClockOff";
private static final String NAMESPACE = "http://tempuri.org/";
private static final String URL = GlobalFunction.URL;
#Override
protected Void doInBackground(String... strArgs)
{
try
{
SoapObject Request = new SoapObject(NAMESPACE, METHOD_NAME);
Request.addProperty("Items", strArgs[0]);
Request.addProperty("ID", strArgs[1]);
SoapSerializationEnvelope soapEnvelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
soapEnvelope.dotNet = true;
soapEnvelope.setOutputSoapObject(Request);
HttpTransportSE transport= new HttpTransportSE(URL);
transport.call(SOAP_ACTION, soapEnvelope);
SoapObject result = (SoapObject) soapEnvelope.bodyIn;
String ReturnMessage = ((SoapObject) result.getProperty(0)).getPropertyAsString(0);
String ReturnMessageHeader = ((SoapObject) result.getProperty(0)).getPropertyAsString(1);
String ActionType = ((SoapObject) result.getProperty(0)).getPropertyAsString(2);
String SelectedCompleted = ((SoapObject) result.getProperty(0)).getPropertyAsString(3);
String ClockOffItems = ((SoapObject) result.getProperty(0)).getPropertyAsString(4);
//Exception thrown on the below line
onAsyncResultClockOffItem.onResultSuccess(ReturnMessage, ReturnMessageHeader, ActionType, SelectedCompleted, ClockOffItems);
return null;
}
catch (Exception e)
{
onAsyncResultClockOffItem.onResultFail();
return null;
}
}
}
OnAsyncResultClockOff is simply:
public interface OnAsyncResultClockOff {
public abstract void onResultFail();
public abstract void onResultSuccess(String returnMessage,
String returnMessageHeader, String actionType,
String selectedCompleted, String clockOffItems);
}
And it is called via:
ClockOff co= new ClockOffItem();
co.setOnResultListener(onAsyncResultClockOff);
co.execute(items, ID);
Where onAsyncResultClockOff implements the OnResultFail() and OnResultSuccess methods.