AppEngine Objectify Java Unit Test gives a weird session cache error as described below.
Test Case:
private final LocalServiceTestHelper helper = new LocalServiceTestHelper(
new LocalDatastoreServiceTestConfig());
protected Closeable session;
#Before
public void setUp() throws Exception {
helper.setUp();
ObjectifyService.setFactory(new ObjectifyFactory());
ObjectifyService.register(UserData.class);
session = ObjectifyService.begin();
}
#After
public void tearDown() throws Exception {
session.close();
helper.tearDown();
}
#Test
public void testUserDataQuery throws Exception {
...
saveUserData();
...
getUserData();
...
}
Call 1:
UserData saveUserData {
...
UserData userData = (UserData) ofy().load().key(key).now();
...
// UserData is modified, the modifications are not stored in datastore,
// as those are temporary.
return userData;
}
Call 2:
UserData getUserData {
...
UserData userData = (UserData) ofy().load().key(key).now();
...
// Return the datastore saved UserData object.
return userData;
}
When the unit test case is executed, the modification done in saveUserData call is seen in getUserData query. Even though ofy().load() has been called, the UserData is not loaded from datastore instead served from the cached entry.
I have tried ofy().clear() call to clear the session cache. This is not avoid the error in all the cases.
This is happening only in Unit Test environment rather than in development or production server.
In the code you posted, yes you'll get the same object back - that's the way the session cache works. Clearing the session cache after save will indeed give you a new object loaded from the datastore (or memcache). But my guess is that this is not what you really want to test.
I'm wildly guessing that you are trying to simulate multiple backend calls in your test. IRL, each backend call will operate in its own server-side context. So what I recommend is creating a context for each call, using closures:
#Test
public void testUserDataQuery throws Exception {
...
req(() -> saveUserData());
...
req(() -> getUserData());
...
}
Where req() does the begin()/close() of the Objectify context (as well as any other request-specific processing your container usually does). You can leave the Objectify initialization in the before/after.
Related
I am trying to update a document in MongoDB but cannot get to checking updated status and responding back to user. Below is my code:
#Autowired
ReactiveMongoTemplate mongoTemplate;
public Mono<String> updateUser(UserIn userIn) {
UserResponse resp = new UserResponse();
mongoTemplate.findAndModify(query, update, User.class)
//.doOnSuccess(bsItem -> {
.flatMap(user -> {
if(user.getItemId().equals(userIn.getId("_id")))
resp.setStatus("Updated");
else
resp.setStatus("Failed");
return Mono.just(resp);
}).subscribe();
return Mono.just(resp.getStatus());
}
Even though update is happening in mongodb, it throws NPE while returning. How to get the control after reactor operator is executed here?
You should almost never subscribe in your own application.
The subscriber is the client that initiated the call in this case it is probably the web application. You application is just relaying the data, so your application is a publisher which means you should not subscribe. The web app subscribes.
Try this.
#Autowired
ReactiveMongoTemplate mongoTemplate;
public Mono<String> updateUser(UserIn userIn) {
return mongoTemplate.findAndModify(query, update, User.class)
.flatMap(user -> {
final UserResponse resp = new UserResponse();
if(user.getItemId().equals(userIn.getId("_id")))
resp.setStatus("Updated");
else
resp.setStatus("Failed");
return Mono.just(resp.getStatus());
});
}
A mono is not like a stream, you fetch, map and return, all in the same mono, like a chain of events. An event chain.
I am trying to integrate Hystrix javanica into my existing java EJB web application and facing 2 issues with running it.
When I try to invoke following service it always returns response from fallback method and I see that the Throwable object in fallback method has "com.netflix.hystrix.exception.HystrixTimeoutException" exception.
Each time this service is triggered, HystrixCommad and fallback methods are called multiple times around 50 times.
Can anyone suggest me with any inputs? Am I missing any configuration?
I am including following libraries in my project.
project libraries
I have setup my aspect file as follows:
<aspectj>
<weaver options="-verbose -showWeaveInfo"></weaver>
<aspects>
<aspect name="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect"/>
</aspects>
</aspectj>
Here is my config.properties file in META-INF/config.properties
hystrix.command.default.execution.timeout.enabled=false
Here is my rest service file
#Path("/hystrix")
public class HystrixService {
#GET
#Path("clusterName")
#Produces({ MediaType.APPLICATION_JSON })
public Response getClusterName(#QueryParam("id") int id) {
ClusterCmdBean clusterCmdBean = new ClusterCmdBean();
String result = clusterCmdBean.getClusterNameForId(id);
return Response.ok(result).build();
}
}
Here is my bean class
public class ClusterCmdBean {
#HystrixCommand(groupKey = "ClusterCmdBeanGroup", commandKey = "getClusterNameForId", fallbackMethod = "defaultClusterName")
public String getClusterNameForId(int id) {
if (id > 0) {
return "cluster"+id;
} else {
throw new RuntimeException("command failed");
}
}
public String defaultClusterName(int id, Throwable e) {
return "No cluster - returned from fallback:" + e.getMessage();
}
}
Thanks for the help.
If you want to ensure you are setting the property, you can do that explicitly in the circuit annotation itself:
#HystrixCommand(commandProperties = {
#HystrixProperty(name = "execution.timeout.enabled", value = "false")
})
I would only recommend this for debugging purposes though.
Something that jumps out to me is that Javanica uses AspectJ AOP, which I have never seen work with new MyBean() before. I've always have to use #Autowired with Spring or similar to allow proxying. This could well just be something that is new to me though.
If you set a breakpoint inside the getClusterNameForId can you see in the stack trace that its being called via reflection (which it should be AFAIK)?
Note you can remove commandKey as this will default to the method name. Personally I would also remove groupKey and let it default to the class name.
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredential(FirebaseCredentials.applicationDefault())
.setDatabaseUrl("https://mkastrive.firebaseio.com")
.build();
FirebaseApp defaultApp = FirebaseApp.initializeApp(options);
DatabaseReference ref = defaultDatabase
.getInstance()
.getReference("users");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("in onDataChange");
System.out.println(dataSnapshot.getValue());
}
#Override
public void onCancelled(DatabaseError databaseError) {
System.out.println("in onCancelled");
System.out.println(databaseError.toString());
}
});
I'm doing the above in the Google Cloud Module in Android. I think my Firebase's initialization is successful because System.out.println("usersRef.push(): " + usersRef.push()); // Working
But I do not see anything for addListenerForSingleValueEvent. I do not see any error/warnings in the logs either. My database rules are set up for anyone to be able to read/write data.
Update 1: According to the suggestion on using setValue(), I tried the example on the firebase's documents:
DatabaseReference usersRef1 = ref.child("users");
Map<String, User> users = new HashMap<String, User>();
users.put("alanisawesome", new User("June 23, 1912", "Alan Turing"));
users.put("gracehop", new User("December 9, 1906", "Grace Hopper"));
usersRef1.setValue(users);
But this is not inserting in the database either, and also no errors. Log's blank.
Update 2:
Some logs
FirebaseApp defaultApp = FirebaseApp.initializeApp(options);
this.defaultDatabase = FirebaseDatabase.getInstance().getReference();
defaultDatabase.child("users").getPath(): https://mkastrive.firebaseio.com
defaultDatabase.child("users").getPath(): /users
Calling push() doesn't make any changes to the database. That's probably why your listener isn't being invoked. push() just returns a DatabaseReference that you can use to make changes at the location represented by that reference. The key of that location (the unique push id) is generated completely on the client.
Try actually writing a value to the database using setValue() on the DatabaseReference returned by push().
I'm having trouble following the second step here.
I really don't understand how this sample does anything other than return a simple toast message. How does it utilize the API to display that message?
class EndpointsAsyncTask extends AsyncTask<Pair<Context, String>, Void, String> {
private static MyApi myApiService = null;
private Context context;
#Override
protected String doInBackground(Pair<Context, String>... params) {
if(myApiService == null) { // Only do this once
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(),
new AndroidJsonFactory(), null)
// options for running against local devappserver
// - 10.0.2.2 is localhost's IP address in Android emulator
// - turn off compression when running against local devappserver
.setRootUrl("http://10.0.2.2:8080/_ah/api/")
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
#Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
});
// end options for devappserver
myApiService = builder.build();
}
context = params[0].first;
String name = params[0].second;
try {
return myApiService.sayHi(name).execute().getData();
} catch (IOException e) {
return e.getMessage();
}
}
#Override
protected void onPostExecute(String result) {
Toast.makeText(context, result, Toast.LENGTH_LONG).show();
}
I'm afraid my this sample is too complex for my limited knowledge. How exactly do I "talk" to the Google Endpoints Module when running an app? Specifically, What is EndpointsAsyncTask();?
Are there any resources listing all the methods available to me? Is there a simpler example of an app communicating with a Google Cloud Endpoint?
The service methods available to you are defined by the backend source in section 1.
In the example you posted, this line: myApiService.sayHi(name).execute()
is an actual invocation call to the backend that you defined by annotating #ApiMethod("sayHi") on the method in the MyEndpoint.java class of your backend module.
The reason your Android app defines an EndpointsAsyncTask is because slow operations such as calls that hit the network need to happen off of the UI thread to avoid locking the UI. The demo simply puts the returned value into a Toast but you could modify onPostExecute() to do whatever you'd like with the result.
For more info on Google Endpoints check out:
https://cloud.google.com/appengine/docs/java/endpoints/
And for info about using an Android AsyncTask look here:
http://developer.android.com/reference/android/os/AsyncTask.html
I have an Spring MVC app with an embedded database (HSQLDB) that I want to initialize after deployment. I know that I could use an xml script to define initial data for my datasource but, as long I'm using JPA + Hibernate, I would like to use Java code. Is there a way to do this?
Heavily updated answer (it was too complex before):
All you need is to add initializing bean to your context, which will insert all the necessary data into the database:
public class MockDataPopulator {
private static boolean populated = false;
#Autowired
private SessionFactory sessionFactory;
#PostConstruct
public void populateDatabase() {
// Prevent duplicate initialization as HSQL is also initialized only once. Duplicate executions
// can happen when the application context is reloaded - e.g. when running unit tests).
if (populated) {
return;
}
// Create new persistence session
Session session = sessionFactory.openSession();
session.setFlushMode(FlushMode.ALWAYS);
// Insert mock entities
session.merge(MockDataFactory.createMyFirstObject())
session.merge(MockDataFactory.createMySeconfObject())
// ...
// Flush and close
session.flush();
session.close();
// Set initialization flag
populated = true;
}
}