Implementing a news feed system in Java EE and jquery - java

I am going to implement a news feed notification system where I will show new items on my webpage. Like this: http://jsfiddle.net/8ND53/.
I will be calling a Servlet say every 5 seconds to get updates and the servlet will return items in json format. That seems very simple, but how do I manage that all on the server side?
What I think is:
I have to keep track of the last item (by date) sent to a user so that next time I can fetch new items from the DB.
Is their any standard way of doing such things?

Just an idea!
I will assume that you make a bean, dao and service layers from your db table that contains the feeds.
At first moment when the page that will retrieve the feeds loads, get all your feeds from a java function and then fill your div with your feeds.
Get the feeds count from your feed container. Now pass the count through jQuery $.post and then to your servlet. To achive this, do the following:
e.g:
//Find divs inside the parent div that will contain your feeds
function getCount(){
return $('#feedContainer').children().find('div').size().toString();
}
//Don't forget to pass it to your jQuery post like this
//(Please, continue reading to understand where to put this)
$.post('/servlet', count:getCount(), function(data){ //manage your new feeds });
Your servlet will receive something like this:
e.g:
String count = request.getParameter("count");
Now that you have the count, verify or just parse as an Integer and then do a list in java. I suppose you have a function to get all your feeds from DB.
e.g:
//Parse it as integer
int feedCountFromUI = Integer.valueOf(count);
//Do a list and call your function to get all feeds from DB.
MyService myService = new MyService();
List<MyBean> feeds = myService.getAll();
//JSON variable
JSONObject result = new JSONObject();
//Fill your JSON variable
for(MyBean mb : feeds) {
//Build a json with your feeds and fill result
}
Suppose that now you have a JSON Object variable called result in your servlet with your feed values filled in the for done above. Now get the count from your list declared as feeds (from above too).
int feedCountFromDB = feeds.size();
You are now close to finish, compare the 2 counts. If the feedCountFromDB it's more than the feedCountFromUI, means that there are new feeds to load.
if(feedCountFromDB > feedCountFromUI){
//Send your json variable (result) to your jquery post callback
out.println(result);
} else {
//Means there is nothing new, print 0
out.println(0);
}
At this point you just need to fill your feed container with the last feed every 5 seconds like this:
(You can add also your jQuery animate function from your jsFiddle at this point)
setInterval(function(){
$.post('/servlet', count:getCount(), function(data){
//If data has feeds (it's not 0)
if(data != 0) {
var lastFeed = data[data.length - 1];
$('#feedContainer').append('<div>' + lastFeed.title + '<br/>' + lastFeed.description + '</div>';
}
});
}, 5000);
This will always check the count from your web interface and then compare it with the count from DB each 5 seconds to get your new feed.
Hope this helps :-)

Related

Waiting for all results before returning Android

I have a little issues, I have make a request to my database (Firebase) to get a set of ids, these ids are live inside a subcollection. When these ids are returned, I will use these ids to get the objects they correspond to. Here is my code
List<DocumentSnapshot> temp = input.getDocuments();
for (DocumentSnapshot snapshot: temp) {
String journeyId = snapshot.get("id").toString();
//make another request to database
//get the result and add to returnobject
}
return returnObject;
Where input is of type QuerySnapshot. I would really like to not return the returnObject till all the objects have been returned from the database. I have a background with Promises in node and using Promise.map is there an equivalent in Android?

Order a ParseQuery by the date at which only one field has been updated

I have a feed containing posts that are currently ordered by "updatedAt". My original intention was to push up posts to the top of the feed which were last replied to (I do this by incrementing a "replyCount" field in each post when a user leaves a comment), not totally cognizant of the fact that another field, "likeCount" is also being updated when user's "like" a post. I would still like to push those posts that were recently "replied to" to the top of the feed, but do not at the expense of the weird UX behavior that associated with liking posts pushing them up as well. I'm not sure how to separate the two.
What should I do here? Can I maybe add another column called "lastReplyCountTime" and then sort queries based on that? Maybe set lastReplyCountTime for all posts to the current time when saved to the database, and then only update that value when a post receives a reply?
String groupId = ParseUser.getCurrentUser().getString("groupId");
ParseQuery<ParseObject> query = new ParseQuery<>(ParseConstants.CLASS_POST);
query.whereContains(ParseConstants.KEY_GROUP_ID, groupId);
/*query.addDescendingOrder(ParseConstants.KEY_CREATED_AT);*/
query.orderByDescending("updatedAt");
query.findInBackground((posts, e) -> {
if (mSwipeRefreshLayout.isRefreshing()) {
mSwipeRefreshLayout.setRefreshing(false);
}
if (e == null) {
// We found messages!
mPosts = posts;
String[] usernames;
usernames = new String[mPosts.size()];
int i = 0;
for(ParseObject post : mPosts) {
usernames[i] = yeet.getString(ParseConstants.KEY_SENDER_NAME);
i++;
}
FeedAdapter adapter = new FeedAdapter(
getListView().getContext(),
mYeets);
setListAdapter(adapter);
}
});
}
You have 2 options:
Like you suggested you can create another date property let's call it sortedUpdatedAt and update it with the current date each time you are updating the relevant values
If you still want to use updatedAt you can wrap your like object in a separate parse object (class). This class will be saved as a relation in the parent class and then each time the user "like" you can just update only this class and not the whole object. This way the updatedAt of your parent object will not be changed.
I think that option 1 is great since it's not so complicated and you can do it very quick.

How to wait for a method with result callback to complete before continuing (Android)?

So I am a noob at Android, and I'm writing a simple app that uses Google Fit to store the users fitness sessions and step count and then retrieve them.
I have two methods, one that fetches all the sessions from a given date range from the cloud, the next method iterates through these and adds up the step count.
Problem is, that although I call the the fetching method first, the result doesn't come back until after I've added the steps up, so step count is always zero.
Here's my code:
private ArrayList<> results;
#Override
public ArrayList<IndividualSession> readAllSessions(Date dateFrom, Date dateTo) {
/* I haven't included the following code in this question just to keep things clean, but here there was
- The initialisation of the results ArrayList
- Creating the calendar and date objects
- Building the session read request
*/
Fitness.SessionsApi.readSession(mGoogleApiClient, readRequest).setResultCallback(new ResultCallback<SessionReadResult>() {
#Override
public void onResult(SessionReadResult sessionReadResult) {
for (Session session : sessionReadResult.getSessions()) {
List<DataSet> dataSets = sessionReadResult.getDataSet(session);
for (DataSet dataSet : dataSets) {
for (DataPoint dataPoint : dataSet.getDataPoints()) {
// Create new IndividualSession object, add data to it then add it to arraylist
IndividualSession individualSessionObject = new IndividualSession();
individualSessionObject.setFromDate(new Date(session.getStartTime(TimeUnit.SECONDS)));
individualSessionObject.setToDate(new Date(session.getEndTime(TimeUnit.SECONDS)));
individualSessionObject.setStepCount(dataPoint.getValue(Field.FIELD_STEPS).asInt());
results.add(individualSessionObject);
}
}
}
Log.i(TAG, "Number of sessions found while reading: "+results.size());
}
});
return results;
}
#Override
public int getDaySteps(Date dateTo) {
int stepCount = 0; // to be returned
// Sort out the dates
Calendar calFrom = Calendar.getInstance();
calFrom.add(Calendar.HOUR, -24);
// Get the sessions for appropriate date range
ArrayList results = readAllSessions(calFrom.getTime(), dateTo);
Log.i(TAG, "Number of sessions found while trying to get total steps: "+results.size());
// Iterate through sessions to get count steps
Iterator<IndividualSession> it = results.iterator();
while(it.hasNext())
{
IndividualSession obj = it.next();
stepCount += obj.getStepCount();
}
return stepCount;
}
This outputs
"Number of sessions found while trying to get total steps: 0"
"Number of sessions found while reading: 8"
There are two solutions to this :
Option 1 : Use a blocking colleciton
Change the ArrayList<> results to an ArrayBlockingQueue<> results.
After the call to the readAllSessions method in the getDaySteps method, call while(results.take()!=null) { //rest of the logic }
You need some kind of mechanistm to exit the while loop in step 2 when all results are read
Option 2 : Use the await method from PendingResult
Looking at the documentation for SessionsAPI class, the readSessions method seems to return a PendingResult :
public abstract PendingResult readSession
(GoogleApiClient client, SessionReadRequest request)
Reads data from the user's Google Fit store of the specific type(s)
and for the specific session(s) selected from the request parameters.
Looking at the documentation of the await method in PendingResult class :
public abstract R await ()
Blocks until the task is completed. This is not allowed on the UI thread. The returned result object can have an additional failure mode
of INTERRUPTED.
This is what you can do. Instead of chaining the entire call to setResultCallBack, first call readSessions :
results = Fitness.SessionsApi.readSession(mGoogleApiClient, readRequest);
And then wait for the results in the getDaySteps method :
SessionReadResults sessionResults = results.await();
for (Session session : sessionReadResult.getSessions()) {
List<DataSet> dataSets = sessionReadResult.getDataSet(session);
for (DataSet dataSet : dataSets) {
for (DataPoint dataPoint : dataSet.getDataPoints()) {
// Create new IndividualSession object, add data to it then add it to arraylist
IndividualSession individualSessionObject = new IndividualSession();
individualSessionObject.setFromDate(new Date(session.getStartTime(TimeUnit.SECONDS)));
individualSessionObject.setToDate(new Date(session.getEndTime(TimeUnit.SECONDS)));
individualSessionObject.setStepCount(dataPoint.getValue(Field.FIELD_STEPS).asInt());
//use the results
}
}
}
*results must be declared as an instance/class level variable to be accessible in all the methods in the class. The variable result is of type PendingResult<SessionReadResults>. Also, looks like you can do away with the results ArrayList since everything you want can be extracted from the SessionReadResults returned by the await method. One last note, this answer has not been tested with your code because your code sample is not complete.

Java Jersey REST Request Parameter Sanitation

I'm trying to make sure my Jersey request parameters are sanitized.
When processing a Jersey GET request, do I need to filter non String types?
For example, if the parameter submitted is an integer are both option 1 (getIntData) and option 2 (getStringData) hacker safe? What about a JSON PUT request, is my ESAPI implementation enough, or do I need to validate each data parameter after it is mapped? Could it be validated before it is mapped?
Jersey Rest Example Class:
public class RestExample {
//Option 1 Submit data as an Integer
//Jersey throws an internal server error if the type is not Integer
//Is that a valid way to validate the data?
//Integer Data, not filtered
#Path("/data/int/{data}/")
#GET
#Produces(MediaType.TEXT_HTML)
public Response getIntData(#PathParam("data") Integer data){
return Response.ok("You entered:" + data).build();
}
//Option 2 Submit data as a String, then validate it and cast it to an Integer
//String Data, filtered
#Path("/data/string/{data}/")
#GET
#Produces(MediaType.TEXT_HTML)
public Response getStringData(#PathParam("data") String data) {
data = ESAPI.encoder().canonicalize(data);
if (ESAPI.validator().isValidInteger("data", data, 0, 999999, false))
{
int intData = Integer.parseInt(data);
return Response.ok("You entered:" + intData).build();
}
return Response.status(404).entity("404 Not Found").build();
}
//JSON data, HTML encoded
#Path("/post/{requestid}")
#POST
#Consumes({MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON})
#Produces(MediaType.TEXT_HTML)
public Response postData(String json) {
json = ESAPI.encoder().canonicalize(json);
json = ESAPI.encoder().encodeForHTML(json);
//Is there a way to iterate through each JSON KeyValue and filter here?
ObjectMapper mapper = new ObjectMapper();
DataMap dm = new DataMap();
try {
dm = mapper.readValue(json, DataMap.class);
} catch (Exception e) {
e.printStackTrace();
}
//Do we need to validate each DataMap object value and is there a dynamic way to do it?
if (ESAPI.validator().isValidInput("strData", dm.strData, "HTTPParameterValue", 25, false, true))
{
//Is Integer validation needed or will the thrown exception be good enough?
return Response.ok("You entered:" + dm.strData + " and " + dm.intData).build();
}
return Response.status(404).entity("404 Not Found").build();
}
}
Data Map Class:
public class DataMap {
public DataMap(){}
String strData;
Integer intData;
}
The short answer is yes, though by "filter" I interpret it as "validate," because no amount of "filtering" will EVER provide you with SAFE data. You can still run into integer overflows in Java, and while those may not have immediate security concerns, they could still put parts of your application in an unplanned for state, and hacking is all about perturbing the system in ways you can control.
You packed waaaaay too many questions into one "question," but here we go:
First off, the lines
json = ESAPI.encoder().canonicalize(json);
json = ESAPI.encoder().encodeForHTML(json);
Aren't doing what you think they're doing. If your JSON is coming in as a raw String right here, these two calls are going to be applying mass rules across the entire string, when you really need to handle these with more surgical precision, which you seem to at least be subconsciously aware of in the next question.
//Is there a way to iterate through each JSON KeyValue and filter
here?
Partial duplicate of this question.
While you're in the loop discussed here, you can perform any data transformations you want, but what you should really be considering is using the JSONObject class referenced in that first link. Then you'll have JSON parsed into an object where you'll have better access to JSON key/value pairs.
//Do we need to validate each DataMap object value and is there a
dynamic way to do it?
Yes, we validate everything that comes from a user. All users are assumed to be trained hackers, and smarter than you. However if you handled filtering before you do your data mapping transformation, you don't need to do it a second time. Doing it dynamically?
Something like:
JSONObject json = new JSONObject(s);
Iterator iterator = json.keys();
while( iterator.hasNext() ){
String data = iterator.next();
//filter and or business logic
}
^^That syntax is skipping typechecks but it should get you where you need to go.
/Is Integer validation needed or will the thrown exception be good
enough?
I don't see where you're throwing an exception with these lines of code:
if (ESAPI.validator().isValidInput("strData", dm.strData, "HTTPParameterValue", 25, false, true))
{
//Is Integer validation needed or will the thrown exception be good enough?
return Response.ok("You entered:" + dm.strData + " and " + dm.intData).build();
}
Firstly, in java we have autoboxing which means this:
int foo = 555555;
String bar = "";
//the code
foo + bar;
Will be cast to a string in any instance. The compiler will promote the int to an Integer and then silently call the Integer.toString() method. Also, in your Response.ok( String ); call, THIS is where you're going to want to encodeForHTML or whatever the output context may be. Encoding methods are ALWAYS For outputting data to user, whereas canonicalize you want to call when receiving data. Finally, in this segment of code we also have an error where you're assuming that you're dealing with an HTTPParameter. NOT at this point in the code. You'll validate http Parameters in instances where you're calling request.getParameter("id"): where id isn't a large blob of data like an entire JSON response or an entire XML response. At this point you should be validating for things like "SafeString"
Usually there are parsing libraries in Java that can at least get you to the level of Java objects, but on the validation side you're always going to be running through every item and punting whatever might be malicious.
As a final note, while coding, keep these principles in mind your code will be cleaner and your thought process much more focused:
user input is NEVER safe. (Yes, even if you've run it through an XSS filter.)
Use validate and canonicalize methods whenever RECEIVING data, and encode methods whenever transferring data to a different context, where context is defined as "Html field. Http attribute. Javascript input, etc...)
Instead of using the method isValidInput() I'd suggest using getValidInput() because it will call canonicalize for you, making you have to provide one less call.
Encode ANY time your data is going to be passed to another dynamic language, like SQL, groovy, Perl, or javascript.

Problems passing data to jquery's getJSON() - Will not accept map

I am trying to serialize my form (JSP/Struts 1.1) and put it into an object or map or whatever jQuery's .getJSON() method needs. Here is my js code:
// This function makes an AJAX call, passing the entire form to the Action class
function ajaxCallWithForm(inputURL, formName, onReturnFunction)
{
var formAsMap = serializeForm(formName);
$.getJSON(inputURL, formAsMap, onReturnFunction);
}
function serializeForm(formName)
{
var obj = {};
var a = $('#'+formName).serializeArray();
$.each(a, function() {
if (obj[this.name] !== undefined) {
if (!obj[this.name].push) {
obj[this.name] = [obj[this.name]];
}
obj[this.name].push(this.value || '');
} else {
obj[this.name] = this.value || '';
}
});
return obj;
}
This results in a java.lang.IllegalArgumentException on the back end (something to do with the BeanUtils.populate servlet method).
If I set the 2nd of 3 parameters of my .getJSON() call to something like this, it works fine and the data shows up in the form object in my Java back end:
// This function makes an AJAX call, passing the entire form to the Action class
function ajaxCallWithForm(inputURL, formName, onReturnFunction)
{
$.getJSON(inputURL, {"vehicleKeyNum":12345,
"vehicleID":"12345",
"rand":Math.random()},
onReturnFunction);
}
I have also tried creating a string with the proper syntax that includes the data from the form and that results in the same thing. I may have my syntax wrong for that. At any rate, my main problem is that:
1) The .getJSON() method accepts, "A map or string that is sent to the server with the request." as its 2nd parameter (see http://api.jquery.com/jQuery.getJSON/)
2) I am passing what I think is a "map"
3) I am getting a java.lang.IllegalArgumentException and don't know where to go from here
If you want to submit a form to server, you can simply use jQuery's serialize() OR serializeArray() method.
$.getJSON(inputURL, $(formName).serialize(), onReturnFunction);
You should have the data returned by the serialize/serializeArray method populated in your form bean if the element names are matched right.
here is a working example of serialize method (copied from jQuery website)
java.lang.IllegalArgumentException from the BeanUtils.populate servlet method is due to data type mismatch between the data submitted and the data on the form bean.

Categories