Volley requestQueue in Singleton returns null - java

I am making a network request with Volley and I am using but which was working very well until suddenly it started throwing Null pointer exception on the requestQueue.
class VolleySingleton(context: Context) {
companion object{
#Volatile
private var newInstance: VolleySingleton? = null
fun getInstance(context: Context) =
newInstance
?: synchronized(this){
newInstance
?: VolleySingleton(context).also{
newInstance = it
}
}
}
private val requestQueue: RequestQueue by lazy{
Volley.newRequestQueue(context) // throws NullPointer exception
}
fun<T> addToRequestQueue(req: Request<T>){
requestQueue.add(req)
}
}
I have tried to initialize it like this
private val requestQueue: RequestQueue =
Volley.newRequestQueue(context) // throws NullPointer exception
but it won't work still.

You should use context.getApplicationContext() instead of context.
// Add a request (in this example, called stringRequest) to your RequestQueue.
VolleySingleton.getInstance(context.getApplicationContext()).addToRequestQueue(request)

I do this in the Application class which is global and stays in scope for the life of the app session. This is in Java, but you might see something I'm doing that you are not.
public class MyApplication extends Application {
/**
* Log or request TAG
*/
public static final String TAG = "MyApp";
/**
* Global request queue for Volley
*/
private RequestQueue mRequestQueue;
/**
* A singleton instance of the application class for easy access in other places
*/
private static MyApplication sInstance;
#Override
public void onCreate() {
super.onCreate();
// initialize the singleton
sInstance = this;
}
/**
* #return MyApplication singleton instance
*/
public static synchronized MyApplication getInstance() {
return sInstance;
}
/**
* #return The Volley Request queue, the queue will be created if it is null
*/
public RequestQueue getRequestQueue() {
// lazy initialize the request queue, the queue instance will be
// created when it is accessed for the first time
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
/**
* Adds the specified request to the global queue, if tag is specified
* then it is used else Default TAG is used.
*
* #param req
* #param tag
*/
public <T> void addToRequestQueue(Request<T> req, String tag) {
// set the default tag if tag is empty
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
VolleyLog.d("Adding request to queue: %s", req.getUrl());
getRequestQueue().add(req);
}
/**
* Adds the specified request to the global queue using the Default TAG.
*
* #param req
* #param tag
*/
public <T> void addToRequestQueue(Request<T> req) {
// set the default tag if tag is empty
req.setTag(TAG);
getRequestQueue().add(req);
}
/**
* Cancels all pending requests by the specified TAG, it is important
* to specify a TAG so that the pending/ongoing requests can be cancelled.
*
* #param tag
*/
public void cancelPendingRequests(Object tag) {
if (mRequestQueue != null) {
mRequestQueue.cancelAll(tag);
}
}
}
Then, you call this in your other Activity code after creating your request object:
MyApplication.getInstance().addToRequestQueue(postRequest);

Related

Java NPE when trying to call method in non-activity class

I have googled here and there for two days already, but no solution seems not be working. Issue is within Fitness.getSessionsClient(this, Objects.requireNonNull(GoogleSignIn.getLastSignedInAccount(2ndclass.mContext))) and its passed activity (this) and context (2ndClass.mContext) variables, which I can populate (variables or not null or empty via debugger inspection) by many found approaches already. Still - all the time I get either NPE (java.lang.NullPointerException) or that getApplicationContext() is null. Any ideas?
Screenshot, if it helps:
Crash log line:
E/<SOME TAG>: Error during PUT connection... Data: {"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"base":"stations","main":{"temp":13.11,"pressure":1033,"humidity":77,"temp_min":10,"temp_max":16.11},"visibility":10000,"wind":{"speed":3.1,"deg":330},"clouds":{"all":69},"dt":1568357213,"sys":{"type":1,"id":1414,"message":0.0113,"country":"GB","sunrise":1568352700,"sunset":1568398909},"timezone":3600,"id":2643743,"name":"London","cod":200} Message: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
Code pieces:
1st (MainActivity) class:
public class 1stClass extends AppCompatActivity {
public static 1stClass instance;
public static Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
instance = this;
mContext = getApplicationContext();
}
public final void 1stmethod(){
2ndClass.2ndmethod();
}
/**
* #return instance
*/
#Contract(pure = true)
public static 1stClass getInstance() { return instance; } // return.getInstance();
public static Context getContext() {
// return instance.getApplicationContext();
return mContext;
}
}
2nd class:
public class 2ndClass extends 1stClass{
static 2ndClass instance;
public final void 2ndmethod() {
instance = this;
3rdClass MFP = new 3rdClass();
MFP.3rdmethod(..);
}
/**
* #return instance
*/
#Contract(pure = true)
public static 2ndClass getInstance() { return instance; } //return.getInstance();
}
Non-activity (3rd) class:
public final class 3rdClass extends 2ndClass {
static 3rdClass instance;
public void 3rdmethod() {
instance = this;
Fitness.getSessionsClient(this, Objects.requireNonNull(GoogleSignIn.getLastSignedInAccount(2ndclass.mContext))) // <---- ERROR HERE
.insertSession(request)
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.i(TAG, "Session insert was successful!");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.wtf(TAG, "There was a problem inserting the session: " +
e.getLocalizedMessage());
}
});
/**
* #return instance
*/
#Contract(pure = true)
public static MFPalInterface getInstance() {
return instance;
} // return.getInstance();
}
I think I understand what you're doing, even though the code is one great mess.
You are trying to create your own activity in your 2ndClass's method '2ndmethod()' and then call the method on that newly created 3rdClass.
public final void 2ndmethod() {
3rdClass MFP = new 3rdClass();
MFP.3rdmethod(..);
}
Even though you've extended 3rdClass from your AppCompatActivity, you've not asked the system to create the Activity instance and attempted to create it yourself. This means the newly created instance has no context and the system does not know about it.
What You Need to Do
Start the activity by calling the startActivity() method of your current running Activity (in this case 2ndClass I think) and by passing an intent. Like so:
Intent intent = new Intent(this, 3rdClass.class); // this is your current Activity's reference.
currentActivity.startActivity(intent);

Recommendation with optimization

How can I achieve more optimization in following code?, I don't like my last check "Objects.isNull" ? Thanks in advance.
/**
* Tries to get the service request DAO from the ItemHolder, if it is not present there, it will try to get it from the argument.
* If no service request found then CancellationException is thrown.
*
* In case both are present takes precedence the one in the ItemHolder object.
*
* #param itemHolderParameter the holder to be checked.
* #param serviceRequestDAO in case no service request dao is found in the holder, this will be used.
* #return ServiceRequestDAO guaranteed not null object.
* #throws CancellationException in case no service request DAO was retrieved.
* #throws IllegalArgumentException in case itemHolderParameter is null.
*/
public static ServiceRequestDAO getServiceRequestDAO(final ItemHolder itemHolderParameter, final ServiceRequestDAO serviceRequestDAO){
AtomicReference<ServiceRequestDAO> atomicReference = new AtomicReference<>(serviceRequestDAO);
final ItemHolder itemHolder = Optional.ofNullable(itemHolderParameter).orElseThrow(() -> new IllegalArgumentException("ItemHolder must not be null"));
Optional.ofNullable(itemHolder.getServiceRequestDAO()).ifPresent(atomicReference::set);
//Final validation.
final ServiceRequestDAO requestDAO = atomicReference.get();
if(Objects.isNull(requestDAO)){
throw new CancellationException("Unable to get ServiceRequestDAO (null)");
}
return requestDAO;
}
~M
There seems to be no good reason to bring Optional or AtomicReference into it:
if (itemHolderParameter == null) {
throw new IllegalArgumentException(...)
}
ServiceRequestDao dao = itemHolder.getServiceRequestDAO();
if (dao != null) {
return dao;
}
if (serviceRequestDao != null) {
return serviceRequestDao;
}
throw new CancellableException(...);
I have done my best to follow the semantics of your code, but honestly, it is very unclear.
Thanks Boris,
/**
* Tries to get the service request DAO from the ItemHolder, if it is not present there, it will try to get it from the argument.
* If no service request found then CancellationException is thrown.
*
* In case both are present takes precedence the one in the ItemHolder object.
*
* #param itemHolderParameter the holder to be checked.
* #param serviceRequestDAO in case no service request dao is found in the holder, this will be used.
* #return ServiceRequestDAO guaranteed not null object.
* #throws CancellationException in case no service request DAO was retrieved.
* #throws IllegalArgumentException in case itemHolderParameter is null.
*/
public static ServiceRequestDAO getServiceRequestDAO(final ItemHolder itemHolderParameter, final ServiceRequestDAO serviceRequestDAO){
return Optional.ofNullable(Optional.ofNullable(itemHolderParameter).map(ItemHolder::getServiceRequestDAO).orElse(serviceRequestDAO)).orElseThrow(() -> new CancellationException("No service request could be retrieved."));
}
The test cases:
public class HolderUtilsTest {
#Test(expected = CancellationException.class)
public void testHolderParameterNulls(){
HolderUtils.getServiceRequestDAO(null, null);
}
#Test(expected = CancellationException.class)
public void testHolderParameterHolderNull(){
HolderUtils.getServiceRequestDAO(new ItemHolder.Builder().build(), null);
}
#Test
public void testHolderParameterNullAndSRNotNull(){
final ServiceRequestDAO serviceRequestDAO = Mockito.spy(new ServiceRequestDAO());
final ServiceRequestDAO serviceRequestDAO1 = HolderUtils.getServiceRequestDAO(new ItemHolder.Builder().build(), serviceRequestDAO);
assertThat(serviceRequestDAO1).isNotNull();
assertThat(serviceRequestDAO1).isEqualTo(serviceRequestDAO);
}
#Test
public void testHolderParameterNotNullWithObjectAndSRNull(){
final ServiceRequestDAO serviceRequestDAO = Mockito.spy(new ServiceRequestDAO());
final ServiceRequestDAO serviceRequestDAO1 = HolderUtils.getServiceRequestDAO(new ItemHolder.Builder().srDao(serviceRequestDAO).build(), null);
assertThat(serviceRequestDAO1).isNotNull();
assertThat(serviceRequestDAO1).isEqualTo(serviceRequestDAO);
}
#Test
public void testHolderBothPresent(){
final ServiceRequestDAO serviceRequestDAO0 = Mockito.spy(new ServiceRequestDAO());
final ServiceRequestDAO serviceRequestDAO1 = Mockito.spy(new ServiceRequestDAO());
final ServiceRequestDAO serviceRequestDAOResult = HolderUtils.getServiceRequestDAO(new ItemHolder.Builder().srDao(serviceRequestDAO0).build(), serviceRequestDAO1);
assertThat(serviceRequestDAOResult).isNotNull();
assertThat(serviceRequestDAOResult).isEqualTo(serviceRequestDAO0);
assertThat(serviceRequestDAOResult).isNotEqualTo(serviceRequestDAO1);
}

Use #Test annotation and ActivityTestRule with Robotium (Espresso-way)

I wonder how can i mark my test as #Test like in JUnit, because so far I have to use 'test...' name. It means:
#Test
private void creatingAcc(){
instead of
private void testCreatingAcc(){
Thanks in advance
Yes, you can do it. Follow these steps to achieve it:
Go to your's app build.gradle file and add test dependendencies like below:
androidTestCompile 'com.android.support.test:runner:0.4.1'
androidTestCompile 'com.android.support.test:rules:0.4.1'
androidTestCompile 'com.android.support:support-annotations:24.1.1'
compile 'com.jayway.android.robotium:robotium-solo:5.6.1'
Now your build.gradle should look like:
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
applicationId "com.example.piotr.myapplication"
minSdkVersion 15
targetSdkVersion 21
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.1.1'
compile 'com.android.support:design:24.1.1'
androidTestCompile 'com.android.support.test:runner:0.4.1'
androidTestCompile 'com.android.support.test:rules:0.4.1'
androidTestCompile 'com.android.support:support-annotations:24.1.1'
compile 'com.jayway.android.robotium:robotium-solo:5.6.1'
}
Then you need to go to your androidTest directory and create Java class and name it for example MyActivityTestRule
In this file put code below:
#Beta
public class MyActivityTestRule<T extends Activity> extends UiThreadTestRule {
private static final String TAG = "ActivityInstrumentationRule";
private final Class<T> mActivityClass;
public Instrumentation getInstrumentation() {
return mInstrumentation;
}
private Instrumentation mInstrumentation;
private boolean mInitialTouchMode = false;
private boolean mLaunchActivity = false;
private T mActivity;
/**
* Similar to {#link #MyActivityTestRule(Class, boolean, boolean)} but with "touch mode" disabled.
*
* #param activityClass The activity under test. This must be a class in the instrumentation
* targetPackage specified in the AndroidManifest.xml
* #see MyActivityTestRule#MyActivityTestRule(Class, boolean, boolean)
*/
public MyActivityTestRule(Class<T> activityClass) {
this(activityClass, false);
}
/**
* Similar to {#link #MyActivityTestRule(Class, boolean, boolean)} but defaults to launch the
* activity under test once per
* <code>Test</code> method.
* It is launched before the first
* <code>Before</code>
* method, and terminated after the last
* <code>After</code>
* method.
*
* #param activityClass The activity under test. This must be a class in the instrumentation
* targetPackage specified in the AndroidManifest.xml
* #param initialTouchMode true if the Activity should be placed into "touch mode" when started
* #see MyActivityTestRule#MyActivityTestRule(Class, boolean, boolean)
*/
public MyActivityTestRule(Class<T> activityClass, boolean initialTouchMode) {
this(activityClass, initialTouchMode, true);
}
/**
* Creates an {#link MyActivityTestRule} for the Activity under test.
*
* #param activityClass The activity under test. This must be a class in the instrumentation
* targetPackage specified in the AndroidManifest.xml
* #param initialTouchMode true if the Activity should be placed into "touch mode" when started
* #param launchActivity true if the Activity should be launched once per
* <a href="http://junit.org/javadoc/latest/org/junit/Test.html">
* <code>Test</code></a> method. It will be launched before the first
* <a href="http://junit.sourceforge.net/javadoc/org/junit/Before.html">
* <code>Before</code></a> method, and terminated after the last
* <a href="http://junit.sourceforge.net/javadoc/org/junit/After.html">
* <code>After</code></a> method.
*/
public MyActivityTestRule(Class<T> activityClass, boolean initialTouchMode,
boolean launchActivity) {
mActivityClass = activityClass;
mInitialTouchMode = initialTouchMode;
mLaunchActivity = launchActivity;
mInstrumentation = InstrumentationRegistry.getInstrumentation();
}
/**
* Override this method to set up Intent as if supplied to
* {#link android.content.Context#startActivity}.
* <p>
* The default Intent (if this method returns null or is not overwritten) is:
* action = {#link Intent#ACTION_MAIN}
* flags = {#link Intent#FLAG_ACTIVITY_NEW_TASK}
* All other intent fields are null or empty.
*
* #return The Intent as if supplied to {#link android.content.Context#startActivity}.
*/
protected Intent getActivityIntent() {
return new Intent(Intent.ACTION_MAIN);
}
/**
* Override this method to execute any code that should run before your {#link Activity} is
* created and launched.
* This method is called before each test method, including any method annotated with
* <code>Before</code>.
*/
protected void beforeActivityLaunched() {
// empty by default
}
/**
* Override this method to execute any code that should run after your {#link Activity} is
* launched, but before any test code is run including any method annotated with
* <code>Before</code>.
* <p>
* Prefer
* <code>Before</code>
* over this method. This method should usually not be overwritten directly in tests and only be
* used by subclasses of MyActivityTestRule to get notified when the activity is created and
* visible but test runs.
*/
protected void afterActivityLaunched() {
// empty by default
}
/**
* Override this method to execute any code that should run after your {#link Activity} is
* finished.
* This method is called after each test method, including any method annotated with
* <code>After</code>.
*/
protected void afterActivityFinished() {
// empty by default
}
/**
* #return The activity under test.
*/
public T getActivity() {
if (mActivity == null) {
Log.w(TAG, "Activity wasn't created yet");
}
return mActivity;
}
#Override
public Statement apply(final Statement base, Description description) {
return new ActivityStatement(super.apply(base, description));
}
/**
* Launches the Activity under test.
* <p>
* Don't call this method directly, unless you explicitly requested not to launch the Activity
* manually using the launchActivity flag in
* {#link MyActivityTestRule#MyActivityTestRule(Class, boolean, boolean)}.
* <p>
* Usage:
* <pre>
* #Test
* public void customIntentToStartActivity() {
* Intent intent = new Intent(Intent.ACTION_PICK);
* mActivity = mActivityRule.launchActivity(intent);
* }
* </pre>
* #param startIntent The Intent that will be used to start the Activity under test. If
* {#code startIntent} is null, the Intent returned by
* {#link MyActivityTestRule#getActivityIntent()} is used.
* #return the Activity launched by this rule.
* #see MyActivityTestRule#getActivityIntent()
*/
public T launchActivity(#Nullable Intent startIntent) {
// set initial touch mode
mInstrumentation.setInTouchMode(mInitialTouchMode);
final String targetPackage = mInstrumentation.getTargetContext().getPackageName();
// inject custom intent, if provided
if (null == startIntent) {
startIntent = getActivityIntent();
if (null == startIntent) {
Log.w(TAG, "getActivityIntent() returned null using default: " +
"Intent(Intent.ACTION_MAIN)");
startIntent = new Intent(Intent.ACTION_MAIN);
}
}
startIntent.setClassName(targetPackage, mActivityClass.getName());
startIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Log.d(TAG, String.format("Launching activity %s",
mActivityClass.getName()));
beforeActivityLaunched();
// The following cast is correct because the activity we're creating is of the same type as
// the one passed in
mActivity = mActivityClass.cast(mInstrumentation.startActivitySync(startIntent));
mInstrumentation.waitForIdleSync();
afterActivityLaunched();
return mActivity;
}
// Visible for testing
void setInstrumentation(Instrumentation instrumentation) {
mInstrumentation = checkNotNull(instrumentation, "instrumentation cannot be null!");
}
void finishActivity() {
if (mActivity != null) {
mActivity.finish();
mActivity = null;
}
}
/**
* <a href="http://junit.org/apidocs/org/junit/runners/model/Statement.html">
* <code>Statement</code></a> that finishes the activity after the test was executed
*/
private class ActivityStatement extends Statement {
private final Statement mBase;
public ActivityStatement(Statement base) {
mBase = base;
}
#Override
public void evaluate() throws Throwable {
try {
if (mLaunchActivity) {
mActivity = launchActivity(getActivityIntent());
}
mBase.evaluate();
} finally {
finishActivity();
afterActivityFinished();
}
}
}
}
Well, to be honest, it's standard ActivityTestRule with additional getter getInstrumentation(), which I would used in my test class.
Create a new Java class - it would be your test class and put the code below:
#RunWith(AndroidJUnit4.class)
public class MainActivityTest {
private Solo solo;
private static final String MAIN_ACTIVITY = MainActivity.class.getSimpleName();
#Rule
public MyActivityTestRule<MainActivity> mActivityRule = new MyActivityTestRule<>(MainActivity.class);
#Before
public void setUp() throws Exception {
solo = new Solo(mActivityRule.getInstrumentation(), mActivityRule.getActivity());
}
#Test
public void checkIfMainActivityIsProperlyDisplayed() throws InterruptedException {
solo.waitForActivity("MainActivity", 2000);
solo.assertCurrentActivity(mActivityRule.getActivity().getString(
R.string.error_no_class_def_found, MAIN_ACTIVITY), MAIN_ACTIVITY);
solo.getText("Hello World").isShown();
}
}
As you see it's not difficult as might be.
Hope it will help

Java Akka Actors with parameters with Spring

In my project, I have used as a code base the Lightbend activator template. It works perfect but the Actor in example is not created with parameters.
I need to create a new Actor and pass to it a parameter during construction such as :
getContext().actorOf(SpringExtProvider.get(actorSystem).props("ControllerActor",type), "controller_" + type)
In this use case, the controller needs to be able to be created with a props paremeter type which is used to typed (obviously) the controller. Each Actor is specifically design to handle and control a specific king of object depending on its type.
But I can't add a new parameter in the props method to pass this parameter. It is not working.
This is my code :
SpringExtension.java
package com.orange.spectre.core.akka.configuration;
import akka.actor.AbstractExtensionId;
import akka.actor.ExtendedActorSystem;
import akka.actor.Extension;
import akka.actor.Props;
import com.orange.spectre.core.config.SpringActorProducer;
import org.springframework.context.ApplicationContext;
/**
* Created by Hervé Darritchon on 04/04/2016.
* <p>
* An Akka Extension to provide access to Spring managed Actor Beans.
*/
public class SpringExtension extends AbstractExtensionId<SpringExtension.SpringExt> {
/**
* The identifier used to access the SpringExtension.
*/
public static SpringExtension SpringExtProvider = new SpringExtension();
/**
* Is used by Akka to instantiate the Extension identified by this
* ExtensionId, internal use only.
*/
#Override
public SpringExt createExtension(ExtendedActorSystem system) {
return new SpringExt();
}
/**
* The Extension implementation.
*/
public static class SpringExt implements Extension {
private volatile ApplicationContext applicationContext;
/**
* Used to initialize the Spring application context for the extension.
*
* #param applicationContext
*/
public void initialize(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Create a Props for the specified actorBeanName using the
* SpringActorProducer class.
*
* #param actorBeanName The name of the actor bean to create Props for
* #return a Props that will create the named actor bean using Spring
*/
public Props props(String actorBeanName) {
return Props.create(SpringActorProducer.class,
applicationContext, actorBeanName);
}
public Props props(String actorBeanName, String type) {
return Props.create(SpringActorProducer.class,
applicationContext, actorBeanName,type);
}
}
}
SpringActorProducer
package com.orange.spectre.core.config;
import akka.actor.Actor;
import akka.actor.IndirectActorProducer;
import org.springframework.context.ApplicationContext;
/**
* Created by Hervé Darritchon on 21/03/2016.
*/
public class SpringActorProducer implements IndirectActorProducer {
private final ApplicationContext applicationContext;
private final String actorBeanName;
private final String type;
public SpringActorProducer(ApplicationContext applicationContext,
String actorBeanName) {
this.applicationContext = applicationContext;
this.actorBeanName = actorBeanName;
this.type = null;
}
public SpringActorProducer(ApplicationContext applicationContext,
String actorBeanName, String type) {
this.applicationContext = applicationContext;
this.actorBeanName = actorBeanName;
this.type = type;
}
#Override
public Actor produce() {
return (Actor) applicationContext.getBean(actorBeanName);
}
#Override
public Class<? extends Actor> actorClass() {
return (Class<? extends Actor>) applicationContext.getType(actorBeanName);
}
}
I can't create an actor with a props parameter as it is possible basically with Akka like (Documentation) :
public class DemoActor extends UntypedActor {
/**
* Create Props for an actor of this type.
* #param magicNumber The magic number to be passed to this actor’s constructor.
* #return a Props for creating this actor, which can then be further configured
* (e.g. calling `.withDispatcher()` on it)
*/
public static Props props(final int magicNumber) {
return Props.create(new Creator<DemoActor>() {
private static final long serialVersionUID = 1L;
#Override
public DemoActor create() throws Exception {
return new DemoActor(magicNumber);
}
});
}
final int magicNumber;
public DemoActor(int magicNumber) {
this.magicNumber = magicNumber;
}
#Override
public void onReceive(Object msg) {
// some behavior here
}
}
system.actorOf(DemoActor.props(42), "demo");
If you can help me, it should be great !
Thanks.
I'm agree with "nickebbitt". Not sure that it is spossible. And one of the way is to inject an implementation of magic number generator into the actor.
Furthermore, I wouldlike to suggest following IndirectActorProducer implementation:
public class SpringDIActor implements IndirectActorProducer {
private static final Logger LOG = LoggerFactory.getLogger(SpringDIActor.class);
private Class<? extends Actor> type;
private Actor actorInstance = null;
public SpringDIActor(Class<? extends Actor> type) {
this.type = type;
}
public SpringDIActor(Actor actorInstance) {
this.actorInstance = actorInstance;
}
/**
* This factory method must produce a fresh actor instance upon each
* invocation. <b>It is not permitted to return the same instance more than
* once.</b>
*/
#Override
public Actor produce() {
Actor newActor = actorInstance;
actorInstance = null;
if (newActor == null) {
try {
newActor = type.newInstance();
} catch (InstantiationException e) {
LOG.error("Unable to create actor of type:{}", type, e);
} catch (IllegalAccessException e) {
LOG.error("Unable to create actor of type:{}", type, e);
}
}
ApplicationContextProvider.getApplicationContext().getAutowireCapableBeanFactory().autowireBean(newActor);
return newActor;
}
/**
* This method is used by [[Props]] to determine the type of actor which will
* be created. This means that an instance of this `IndirectActorProducer`
* will be created in order to call this method during any call to
* [[Props#actorClass]]; it should be noted that such calls may
* performed during actor set-up before the actual actor’s instantiation, and
* that the instance created for calling `actorClass` is not necessarily reused
* later to produce the actor.
*/
#Override
public Class<? extends Actor> actorClass() {
return type;
}}
This allows you co create actors without direct accesing to SpringContext from any code as follows:
ActorSystem.create("system").actorOf(Props.create(SpringDIActor.class, DemoActor.class))
Then just to use #Autowired annotation into the DemoActor.
No additional annotation on DemoActor is not required.

Rest client design decisions

I'm developing a rest client using retrofit. The rest server is using oauth as authentication. For this task I don't have to care about the token expiring. So basically I first make a request for the token and then append that to all subsequent calls.
As of now I'm using two classes. One to get the access token and another one for everything else. I think I would like to merge these two... but I'm not sure how to do that.
The first class only has one method which takes a username and password and a retrofit callback interface. I like the simplicity of the callback but would like to somehow abstract it so I could easily change from retrofit to something else if needed.
public class RequestAccessToken implements IRequestAccessToken {
private String username;
private String password;
private IRestAPI client;
public RequestAccessToken()
{
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Config.ENDPOINT)
.build();
client = restAdapter.create(IRestAPI.class);
}
#Override
public void requestAccessToken(String username, String password, Callback callback) {
String grantType = Config.grantType;
String clientId = Config.clientId;
String clientSecret = Config.clientSecret;
client.getAccessToken(grantType, username, password, clientId, clientSecret, callback);
}
}
The second class takes the access token as constructor argument and appends it to all http requests.
public class RestClient implements IRestClient {
private static final String TAG = RestClient.class.getSimpleName();
private IRestAPI client;
public RestClient(final String accessToken)
{
RequestInterceptor requestInterceptor = new RequestInterceptor()
{
#Override
public void intercept(RequestFacade request) {
request.addHeader("Authorization", "Bearer " + accessToken);
}
};
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Config.ENDPOINT)
.setRequestInterceptor(requestInterceptor)
.build();
client = restAdapter.create(IRestAPI.class);
}
#Override
public List<User> requestUsers() {
return client.requestUsers();
}
#Override
public List<Soemthing> requestSomething() {
return client.requestSomething();
}
#Override
public List<SoemthingElse> requestSomethingElse() {
return client.requestSomethingElse();
}
}
I would love some input and suggestions on how to do this better and perhaps merge the two classes. I'm thinking of making the requestAccessToken method of the RequestAccessToken a static member of the RestClient class. At least that would merge the two class. But I'm using a factory to create the RestClient and if I declare a static method on it which I use throughout my code I get tight coupling... Suggestions?
After discussing this elsewhere I came up with the following code.
public class RestClient implements IRestClient {
private static final String TAG = RestClient.class.getSimpleName();
private IRestAPI client;
private String username;
private String password;
private RequestAccessToken requestAccessToken;
private Access access;
private static RestClient singleton;
// TODO: use Dagger
public static RestClient getInstance()
{
if(singleton == null)
{
singleton = new RestClient();
}
return singleton;
}
/**
* Access declared as private to prevent instantiation outside of this class.
*/
private RestClient()
{
requestAccessToken = new RequestAccessToken();
}
/**
* Request an access token.
*
* #return A string containing the access token
*/
public String requestAccessToken(String username, String password)
{
this.username = username;
this.password = password;
this.access = requestAccessToken.requestAccessToken(username, password);
return this.access.getAccess_token();
}
/**
* Wrapper for {#link #requestAccessToken(String, String) requestAccessToken}
* #return
*/
public String requestAccessToken()
{
if(username == null || password == null) {
throw new IllegalArgumentException("missing user/pass");
}
return requestAccessToken(username, password);
}
/**
* Eventually we want to look at Access.getExpires_in() and return weather or not the token is
* expired.
*
* #return value indicating weather or not the token is expired.
*/
public boolean isAccessTokenExpired()
{
return false;
}
/**
* Return an instance of a rest client with a valid access token.
* #return
*/
private IRestAPI getClient()
{
if(access == null)
{
requestAccessToken();
}
if(isAccessTokenExpired())
{
// Refresh token
}
if(client == null) {
RequestInterceptor requestInterceptor = new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
request.addHeader("Authorization", "Bearer " + access.getAccess_token());
}
};
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Config.ENDPOINT)
.setLogLevel(RestAdapter.LogLevel.FULL)
.setRequestInterceptor(requestInterceptor)
.build();
client = restAdapter.create(IRestAPI.class);
}
return client;
}
/**
* Login to the api and get a access token.
*
* #param username
* #param password
*/
#Override
public void login(String username, String password)
{
Log.d(TAG, "login");
requestAccessToken(username, password);
}
/**
* Request a list of organizations.
* #return List of Organizations.
*/
#Override
public List<Organization> requestOrganizations() {
Log.d(TAG, "requestOrganizations");
// TODO: error handling
// TODO: caching... which isn't http
return getClient().requestOrganizations();
}
// Add more api requests here
}

Categories

Resources