Bootstrap and Angular

I’ve been adding Bootstrap and Angular to my existing Struts2 site.  They improve the look and feel of the site and make it feel more responsive to the user.  There are some issues though refactoring a Struts2 page-refresh style site to use Bootstrap and Angular. I think it is worth doing it though as you get a much nicer front-end.  Ideally I would like to write the whole front end using Bootstrap and Angular and just use Struts2 as a framework for exposing Json services.  That will require a lot of refactoring but I am slowly moving closer.

 

First of all – what are Bootstrap and Angular and what are their advantages.

 

Bootstrap is a CSS and Javascript framework.  It’s advantages from my point are that it improves the look and feel of the site and, more importantly, it makes it easy to mobile optimise (ie – two columns on a laptop screen becomes two rows on a mobile screen) the site. Also importantly (as someone who has been more comfortable coding a Java back-end than a good-looking HTML and CSS front-end), it is relatively easy to learn to use. Bootstrap was originally developed by a team at Twitter.

 

For an example of how Bootstrap can be used to mobile optimise a page look at this page www.findfriendsforfun.com using a laptop.  The main contents of the page are laid out in two columns each with two rows.  Then shrink the browser down in width.  Note that when it gets to mobile phone width the screen rearranges to be one column with four rows.  Also the top menu disappears and is accessable via a button. This is all done using out of the box Bootstrap CSS classes.

 

For another example look at this page http://www.findfriendsforfun.com/register.action using a laptop.  There is a form with labels on the left and fields on the right. Then shrink the browser down in width.  Note that when it gets to mobile phone width the form rearranges so the labels move to above their fields and the fields take up the whole width of the screen. This is also done using out of the box Bootstrap CSS classes.

 

 

Angular is a Javascript framework that extends HTML.  What this means is that you write all your HTML as HTML (with some extra attributes) and also write a separate Javascript file that controls the HTML and handles calls to the back-end server.  This is in contrast to a framework like JQuery where I frequently found myself writing Javascript that wrote HTML and added it to the page at run time. It makes your website feel more like a native app and much more responsive to a user than a website that uses page-refresh for everything. It also results in cleaner easier to maintain code than JQuery. I have read that it has a bigger learning curve than some frameworks. I haven’t found that to be an issue though. Angular was originally developed by a team at Google.

 

I have only used Angular in bits of http://www.findfriendsforfun.com that are only accessable to users that have logged in – so I can’t share a link to that here (but if you are keen enough – register, complete your profile and then go and have a look at the Search page J ). But a good example is where you are displaying a paging list of items. When the user clicks ‘Next’ the server queries the database to get the next page of data. Using straight Struts2 to display this next page to the user you need to refresh the whole page. This is a lot slower and also means the page flickers and jumps around. Using Angular the Next button makes a Ajax call to a Json service. When the Json service returns the data Angular replaces the old data with the new data. The rest of the page is not touched.  This is faster and feels much nicer for the user.

 

 

Pain-points when applying to an existing Struts2 website.

 

Bootstrap has standard CSS styles for a lot of HTML tags. It also has a lot of CSS classes defined.  These will probably conflict with your existing CSS file. My solution was to delete my existing CSS file and then add back in just what was necessary. This could involve a lot of work if you have a large site with a lot of CSS.

 

Bootstrap has a nice way of displaying forms that also responds to the screen width.  But if you’ve developed all your existing forms using the default Struts2 theme xhtml (you will be using this if you haven’t specified anything else) then you won’t get those advantages. The xhtml theme lays forms out using a table, so it can’t rearrange depending on screen width, and obviously isn’t going to add the Bootstrap look and feel CSS classes unless you tell it too. If the only visible bit of your form is the submit button then you can make it look like a Bootstrap style button just by adding the Bootstrap CSS classes

cssClass=”btn btn-default”

to the s:submit tag. If your form has some visible fields and you want it to respond to the screen width – then one solution is to rewrite the form in straight HTML rather than using Struts tags.

Ie change

<s:form action=”someAction” method=”post” >

<s:textfield name=”someFieldName” key=”some.key.someFieldName” size=”40″/>

<s:submit name=”something” key=”some.key.something” align=”center” />

</s:form>

to

<form id=”contactform” class=”form-horizontal”

action=”someAction”

method=”post”

validate=”true”

role=”form”>

<div class=”form-group”>

<label class=”col-sm-5 control-label” for=”someFieldName”>Some Field</label>

<div class=”col-sm-7″>

<input type=”text” class=”form-control” placeholder=”Some Field” id=”someFieldName” name=”someFieldName” required/>

</div>

</div>

<div class=”form-group”>

<div class=”col-sm-offset-5 col-sm-7″>

<input class=”btn btn-default” type=”submit” value=”Do Something”/>

</div>

</div>

</form>

 

If you choose to rewrite your forms in HTML as above – and you were using Struts2 XML validation files for the form – you’ll suddenly find that they don’t work anymore.  More correctly – they do work – but they aren’t able to display error messages by the fields like they used to.  From a users point of view – they click submit and nothing happens and they get no feedback as to why.  If you want to continue using the Struts2 XML validation it would probably be possible to fix that if you wanted to dive into the HTML code the Struts tags create and add the relevant bits to your form.  What I have done is removed the Struts XML validation completely and use the HTML5 validation options instead (ie <input type=”email” ….. required/> ). This works for most browsers and then I use server side validation for old browsers and malicious users who know how to get around client-side validation.

 

With Angular you can take a existing Struts2 page and change it to a Angular driven page using Ajax calls to update what’s on the page.  It’s great – until you leave it to go to another page and then use the ‘Back’ button to go back to it.  What you get then is the page as it was when the user first went to it.  Ie if the Angular page had a list of paging data on it, the user pages to page 2, then clicks on ‘See this item’ for one of the items in the list, then clicks back – they will go back to the list on the first page of data.  The way I have got around that is to use local storage http://gregpike.net/demos/angular-local-storage/demo/demo.html to save all the results of the Ajax calls made while the user is on the page.  Every time you go to the Angular list page the first thing is does is check the local storage to see what the last state of the page was.  Some points about this approach.

You need to guard against the case where User A uses the website then logs out.  User B then uses the same computer and browser to visit the website.  You don’t want User B to see User A’s data.  To get around this I clear all local storage when a user first logs in and also store the user id with everything I put in local storage and check that.

I found the cookie bits of the angular-local-storage script were buggy.  I created my own version of the script with all the cookie bits commented out.

It won’t work with old browsers.  Old browsers will get the Angular page as it was when the user first visited it.

 

I’ve found Angular has issues with hidden input fields

<input type=”hidden” ….. />

Especially when using Chrome.

So I use

<input type=”text” style=”display: none;” ….. />

 

Advertisements
Posted in Uncategorized | Tagged , , , , | Leave a comment

Refactoring for Struts2 security changes.

I recently upgraded my Struts2 version from 2.3.15.1 to 2.3.16. There are a couple of important security upgrades in this update (http://struts.apache.org/development/2.x/docs/s2-018.html and http://struts.apache.org/development/2.x/docs/s2-019.html) so it is worth upgrading. One of the changes was to turn off Dynamic Method Invocation (DMI). I had been using this feature, so I found that most of my website stopped working with the new release.
I was using DMI by having a method attribute on a submit tag, ie
<s:form action=”register” method=”post”>
<s:submit method=”refreshCaptcha” key=”label.register.refresh” align=”center” />
</s:form>
With DMI turned off the default execute method is being called instead of the specified refreshCaptcha method.
If you’ve hit this problem your options for getting this working again are

1. NOT RECOMMENDED. Turn DMI back on again. Put
<constant name=”struts.enable.DynamicMethodInvocation” value=”true”/>
in your struts.xml file. The downside of this is that DMI is a security issue – so only do this if you don’t care about security.

2. Add the method attribute to the action definition in the struts.xml file.
<s:form action=”register-rc” method=”post”>
<s:submit key=”label.register.refresh” align=”center” />
</s:form>
and in struts.xml
<action name=”register-rc” class=”registerAction” method=”refreshCaptcha” >
<result name=”input” type=”tiles”>/public.register.tiles</result>
<result name=”error” type=”tiles”>/public.register.tiles</result>
<result name=”resent” type=”tiles”>/public.registersuccess.tiles</result>
</action>
The downside of this is that you then need a separate action definition for every method in your action class that you want to call. You also can’t use this approach if you have a form with two submits in it .

3. NOT RECOMMENDED. As an extension to 2 – you can make this work with forms with 2 submits if you put
<constant name=”struts.mapper.action.prefix.enabled” value=”true”/>
in your struts.xml file and then add a separate
action=”register_refresh”
attribute to one of the submits – and add a separate action definition to struts.xml. The downside of this is that setting struts.mapper.action.prefix.enabled to true is reintroducing another security issue. So only do this if you don’t care about security.

4. Use the name attribute on the submit tag rather than the method attribute. This is what I did in most cases. So change the submit tag to use name
<s:form action=”register” method=”post”>
<s:submit name=”refreshCaptcha” key=”label.register.refresh” align=”center” />

</s:form>
Create a boolean in your action class and a setter method
private boolean refreshCaptcha = false;

public void setRefreshCaptcha(boolean refreshCaptcha) {
//this.refreshCaptcha = refreshCaptcha;
this.refreshCaptcha = true;
}
Note: in the setter I am ignoring the value that is passed in and hardcoding the boolean to true. This is because false is passed in unless you use the value attribute on the submit tag. If you use the value attribute – ie
<s:submit name=”refreshCaptcha” value=”Get new picture” align=”center” />
the value of the attribute will be what is displayed on the button (which may not be what you want if you are using key) – and the set method will be called with a String
public void setRefreshCaptcha(String refreshCaptchaStr) {
//you could check for the value of the string – or you could
this.refreshCaptcha = true;
}
All I really need to know is that the method has been called – so hardcoding the value in the method is ok.
Then in your execute method add an if statement at the begining
public String execute() {
if (refreshCaptcha) {
return refreshCaptcha();
}
// rest of the execute method
}
There is a similar approach documented here http://struts.apache.org/release/2.3.x/docs/multiple-submit-buttons.html – although I was never able to get a true value passed to my setter methods as described there.

Posted in Uncategorized | Tagged , , , , | Leave a comment

JSON Services using Struts2

I’ve implemented some of my front-end using javascript (jquery) and needed to expose some JSON services to enable this.  As I’m using Struts2 as my MVC framework.  There is a Struts2-jQuery tag library you can use.  However, this post is just about exposing the Struts2 action as a JSON service so that it can be used by straight jQuery (or whatever javascript library you choose to use).

The jQuery to call the service

var timestamp = new Date().getTime();

var cuurl = “/events_json.action?ts=” + timestamp;

var data = {

action: “search”,

start: startAt,

max: maxResults,

activity: activitySearchFor,

area: area,

country: country,

state: state,

town: town

};

$.post(cuurl,

data,

function(response) {

eventUtil.handleResponse(response);

},

“json”

);

Note –  ?ts=” + timestamp to overcome potential response caching issues.

The struts.xml definition

<action name=“events_json” class=“eventsAction” method=“json” >

</action>

Note 1 – no results are defined

Note 2 – I’m using Spring to define my classes

My Struts action class

public class EventsAction extends FFFFBaseAction

implements ServletResponseAware,

ParameterAware{

Implement the ServletResponseAware and ParameterAware interfaces to be able to access the response object and the parameters.

private static final String JSON_ACTION_PARAM = “action”;

private static final String JSON_STARTAT_PARAM = “start”;

private static final String JSON_MAXRESULTS_PARAM = “max”;

private static final String JSON_ACTIVITY_PARAM = “activity”;

private static final String JSON_AREA_PARAM = “area”;

private static final String JSON_COUNTRY_PARAM = “country”;

private static final String JSON_STATE_PARAM = “state”;

private static final String JSON_TOWN_PARAM = “town”;

Parameter name values

private HttpServletResponse response;

private Map parameters;

@Override

public void setServletResponse(HttpServletResponse response) {

this.response = response;

}

@Override

public void setParameters(Map parameters) {

this.parameters = parameters;

}

The json method.  This is a common entry point for multiple json services

public String json() {

String toRet = null;

getValuesFromSession();

String[] actions = (String[])parameters.get(JSON_ACTION_PARAM);

String action = getStringValFromParam(actions);

logger.debug(“action is ” + action);

String jsonString = “”;

// common object I am using to wrap the data my services return

JsonResult jResult = null;

// I’m using this method as a common entry point for many json services

if (“search”.equals(action)) {

jResult = search();

} else if (“next”.equals(action)) {

jResult = next();

} else if (“previous”.equals(action)) {

jResult = previous();

} else {

logger.error(“json method called but action not set or not recognised – action is” + action);

jResult = new JsonResult();

jResult.setError(true);

jResult.setErrorMessage(“Sorry, there as an unexpected problem”);

}

try {

// I am using the google gson library to convert from java beans

// to a json string

Gson gson = new Gson();

jsonString = gson.toJson(jResult);

//logger.debug(“A json string ” + jsonString);

} catch (Exception e) {

logger.error(“gson can’t cope with the results”, e);

// try again, to return error

jResult = new JsonResult();

jResult.setError(true);

jResult.setErrorMessage(“Sorry, there as an unexpected problem”);

try {

Gson gson = new Gson();

jsonString = gson.toJson(jResult);

} catch (Exception eAgain) {

logger.error(“gson can’t cope with the results again”, eAgain);

}

}

try {

response.setCharacterEncoding(“UTF-8”); // to deal with special characters

PrintWriter pw = response.getWriter();

// this response.printwriter.write call is where the json string

// is returned to the calling javascript

pw.write(jsonString);

} catch (IOException ioe) {

logger.error(“there was IOException in json method”, ioe);

}

// toRet is null – we can return null.  There are no results

// defined in the xml definition

return toRet;

}

The search method.  This contains the logic for this service

private JsonResult search() {

// the object I will return and then convert to a json string

JsonResult toRet = new JsonResult();

toRet.setError(false);

logger.info(“In search”);

try {

// getting the values that the javascript set as parameters

String[] startAtStrs = (String[])parameters.get(JSON_STARTAT_PARAM);

String startAtStr = getStringValFromParam(startAtStrs);

String[] maxResultsStrs = (String[])parameters.get(JSON_MAXRESULTS_PARAM);

String maxResultsStr = getStringValFromParam(maxResultsStrs);

String[] activityStrs = (String[])parameters.get(JSON_ACTIVITY_PARAM);

String activityStr = getStringValFromParam(activityStrs);

String[] areaStrs = (String[])parameters.get(JSON_AREA_PARAM);

String areaStr = getStringValFromParam(areaStrs);

String[] countryStrs = (String[])parameters.get(JSON_COUNTRY_PARAM);

String countryStr = getStringValFromParam(countryStrs);

String[] stateStrs = (String[])parameters.get(JSON_STATE_PARAM);

String stateStr = getStringValFromParam(stateStrs);

String[] townStrs = (String[])parameters.get(JSON_TOWN_PARAM);

String townStr = getStringValFromParam(townStrs);

…..

// wrapper for our results

EventSearchResult eventSearchResult = new EventSearchResult();

List<EventActivityResult> eaResults = new ArrayList<EventActivityResult>();

eventSearchResult.setEventActivityResults(eaResults);

…..

// Access the database to get some data

List<Event> events = null;

events = eventService.search(activityId, area, meFFFFEntity, countryStr, stateStr, townStr, eventOrderBy, ascDesc, hibernateStartAt, maxResults, ffffEntityIdsNotWanted);

// we don’t want to convert the hibernate entities to json because

// A – that would mean sending more data than we need to – and

// B – lazy loaded hibernate proxies don’t like (will throw exceptions)

// being converted to json

// so here we transform from hibernate entities to java beans

// that contain just the info the front end needs

// so gson doesn’t have to parse a hibernate proxy

List<JsonEvent> jsonEvents = JsonEventTransform.toJsonShortForm(events);

EventActivityResult eaResult = new EventActivityResult();

//ueResult.setEvents(events);

eaResult.setJsonEvents(jsonEvents);

eaResult.setMaxResults(maxResults);

eaResult.setNumberOfResults(count);

eaResult.setStartAt(startAt);

eaResult.setShowingDisplayString(SearchUtil.createShowingDisplayString(hibernateStartAt, maxResults, count));

eaResult.setActivityId(activityId);

eaResult.setActivityName(activityName);

eaResults.add(eaResult);

…..

toRet.setResult(eventSearchResult);

} catch (Exception e) {

logger.error(“there was a exception getting events when user searched ” + meFFFFEntity, e);

toRet.setError(true);

// TODO – where get error message from?

toRet.setErrorMessage(“Sorry, there was an unexpected problem.”);

}

return toRet;

}

Common class I use to wrap data to be converted to json

public class JsonResult {

private boolean error;

private String errorMessage;

private Object result;

// constructor and getter and setter methods

}

Java bean for the event entity that can be converted to json

public class JsonEvent implements Serializable {

private Integer id;

private String title;

private String shortDesc;

private String description;

private boolean cancelled;

private String startDate;

private String country;

private String state;

private String town;

private String formattedAddress;

private JsonFFFFEntity owner;

private List<JsonActivity> activities;

// constructor and getter and setter methods

}

To transform from hibernate entity to java bean

public class JsonEventTransform {

public static List<JsonEvent> toJsonShortForm(List<Event> events) {

// method body

}

Posted in Uncategorized | Tagged , , , | 1 Comment

Hibernate – Saving a Many to Many relationship.

I am mapping my object model using Hibernate annotations.  I came across a issue when trying to save a entity bean to a table, along with a Many-to-Many relationship to a row in another table.  The exception I got was

javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: au.com.ffff.model.Activity

The Activity table is the other table.  The one I just want to save a relationship to.  To save a new entity you need to use entityManager.persist.  To save (update) an existing entity you need to use entityManager.merge.  The entity I am saving is new, but it has a relationship with an existing entity.  The solution is to use persist to save the entity without the relationship, then add the relationship and call merge.

My tables

Event
id
other fields

EventActivity
eventId
activityId

Activity
id
other fields

My entity beans

@Entity
public class Event implements Serializable {

@Id
@GeneratedValue
private Integer id;

@ManyToMany (cascade=CascadeType.ALL,
fetch=FetchType.EAGER)
@JoinTable(name=”EventActivity”,
joinColumns=@JoinColumn(name=”eventId”),
inverseJoinColumns=@JoinColumn(name=”activityId”))
private Set<Activity> activities;

other fields
}

@Entity
public class Activity implements Serializable {

@Id
@GeneratedValue
private Integer id;

@ManyToMany (mappedBy=”activities”,
fetch=FetchType.LAZY)
private Set<Event> events;

other fields
}

My code

Action class method

some code

// hibernate won’t just let me create an instance
// I have to get it from the DB first.
Activity activity = activityService.find(activityId);
Set<Activity> activities = new HashSet<Activity>();
activities.add(activity);
event.setActivities(activities);

eventService.save(event);

more code

Service implementation

some code

Set<Activity> activities = event.getActivities();
// have to do this to keep hibernate happy.
event.setActivities(null);
// this inserts the row into the event table
em.persist(event);

event.setActivities(activities);
// this inserts the row into the eventactivity table
// and would also update the activty table if it had changed
em.merge(event);

more code

This works for me.  It does mean that I have to do one more database query than I really need to.  I can’t just create a Activity instance and set the id (or hibernate will try to update all the other activity fields to null when I call merge).  I have to get the whole activity object out of the database even though the only value I need from it is the id to go into the activityId column of the EventActivity table.

Posted in Uncategorized | Tagged , , | Leave a comment

Hibernate – Pure native scalar queries are not yet supported

I’m using Hibernate annotations for my database access.  This works fine most of the time, but with complex queries the Hibernate hql can get tricky.  When I know exactly how to write the query using sql I sometimes give in and use a Hibernate NamedNativeQuery.  I know this means I will have a little bit of refactoring to do if I ever decide to change the database, but other than that remote possibility, this also seems to work fine and for me is a much faster approach.  However, if the query just returns a number (i.e., the query is something like ‘select count(id) where…..) I came up against this error

 

org.hibernate.cfg.NotYetImplementedException: Pure native scalar queries are not yet supported

 

I couldn’t find a obvious way around this, so came up with the solution below.

 

The NamedNativeQuery.

@Entity

@NamedNativeQueries({

@NamedNativeQuery(name=”countAllForActivity” ,

query=”select count(distinct st.othertablea_Id) as id” +

” FROM SomeTable st where st.othertableb_Id = ?1″,

resultSetMapping=”countWrapper”)

 

The SqlResultSetMapping.

@SqlResultSetMappings({

@SqlResultSetMapping(name=”countWrapper”,

entities={

@EntityResult(entityClass=CountWrapper.class)

})

})

public class SomeTable implements Serializable {

// the SomeTable class contents

}

 

So CountWrapper is an @Entity I have created just to hold the results of count queries.  CountWrapper has one field.  Id.  Hence the query has to select the count ‘AS ID’.  The query then returns the result as a CountWrapper object with the result in the id field.

 

The CountWrapper class

 

@Entity

public class CountWrapper implements Serializable {

@Id

private Integer id;

 

public Integer getId() {

return id;

}

 

public void setId(Integer id) {

this.id = id;

}

}

 

But that is not enough.  Hibernate gets upset if you have a @Entity class without a corresponding table in the database.  So you also have to create a CountWrapper table.

 

Sql to create the CountWrapper table

CREATE TABLE CountWrapper (

id INTEGER UNSIGNED,

PRIMARY KEY(id)

)

 

The CountWrapper table is an empty table.  It never gets populated or queried.  It’s just there to keep hibernate happy (it probably won’t do such a good job of keeping your DBA happy though 🙂 ).

 

It’s then possible to run the query and get the result using the code below

 

public int count(int activityId) {

int toRet = 0;

Query namedQuery = getEntityManager().createNamedQuery(“countAllForActivity”);

namedQuery.setParameter(1, activityId);

Object namedRes = null;

try {

namedRes = namedQuery.getSingleResult();

} catch (NoResultException nre) {

// can ignore this – just return 0

} catch (Exception e) {

logger.error(“unexpected Exception when searching”, e);

throw new FFFFRuntimeException(“problem with search”, e);

}

if (namedRes != null) {

if (namedRes instanceof CountWrapper) {

toRet = ((CountWrapper)namedRes).getId();

} else {

throw new FFFFRuntimeException(“count returned something that was not an integer”);

}

logger.debug(“toRet is ” + toRet);

}

return toRet;

}

 

This works for me.  Hopefully it’ll help someone.  But I can’t help thinking there must be a proper way provided by hibernate to do this.  I just couldn’t find one.

 

Posted in Uncategorized | Tagged , , , | Leave a comment

Including html in Struts2 form tags.

Recently I had an issue using Struts2 tags to create a form that contained a checkbox.  I wanted the label for the checkbox to contain a link (for example – http://www.findfriendsforfun.com/register.action), but the helpful Struts2 tags kept escaping the html.  After several evenings banging my head against this, I eventually I found that the way to fix this was to use themes.  This blog describes a similar issue.

Struts2 uses themes when converting struts tags to html.  Struts2 comes with 3 themes.  They are simple, xhtml and css_xhtml.  If you don’t specify a theme the default is xhtml.  You can set the theme you want to use for a form or field using the attribute ‘theme’

ie

<s:form id=“regform” theme=“simple” action=“register_submit.action” method=“post” validate=“true”/>

xhtml and css_xhtml will both escape the html, and simple won’t even show it.

Luckily you can create your own themes.

The themes are in the jar file struts2-core.jar in the directories template.{themename}.  Each directory contains a number of freemarker script files for the different Struts2 tags.  The script checkbox.ftl is used to create the final html for the Struts2 tag <s:checkbox/>

Create a directory under your src folder called template.{your theme name}.

Extract all files from the jar for the theme you want to extend and copy them to your new directory.

You can now edit the theme file.  To stop escaping html in the label of a checkbox, you need to edit checkbox.ftl.  If you copied the files from the xhtml theme you need to need to change line 120 from

>${parameters.label?html}</label><#rt/>

to

>${parameters.label}</label><#rt/>

so remove the ‘?html’.

Edit your struts.xml file so struts knows where to find your theme.

<constant name=“struts.ui.templateDir” value=“template” />

You now have a choice.  If you want to use your theme as the default for your whole application then also add the below line to your struts.xml

<constant name=”struts.ui.theme” value=”dbe/>

Or you can just use your theme for your form

<s:form id=“regform” theme=“dbe” action=“register_submit.action” method=“post” validate=“true”/>

Or you can just use your theme for the checkbox

<s:checkbox name=“tAndCAgreed” theme=“dbe” key=’I have read and agree to the <a href=“/disclaimer_public.action” target=“_blank”>Terms and Conditions</a>.‘ />

Posted in Uncategorized | Tagged , , , | Leave a comment