====== New Java Web Applications ====== End of life December 2016. If you are making a company project : * You **NEED** nexus access, contact anthony.arents@mentoringsystems.be All new projects have specific requirements in the buildprocess : * Netbeans project * Maven * Spring (3.0.5.RELEASE -> standard) * Spring Security (3.0.5.RELEASE -> standard) * Hibernate (3.6.10.FINAL -> standard) * Javamelody (extra used for statistics, developer tool) * ExtJS 5 * Tomcat 7 * Use Java 7 for compiling, I recommend Java 8 for running tomcat (no permgen errors) ;-). Tomcat settings \\ -Xmx2048m -Xms512m -XX:MaxPermSize=768m -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true ====== Package structure : ====== ''be.mentoringsystems.applicationname.dao'' Contains DAO interfaces, naming ObjectnameDAO ''be.mentoringsystems.applicationname.dao.hibernate'' Contains DAO implementations, naming HibernateObjectnameDAO, extends HibernateDaoSupport, implements ObjectnameDAO ''be.mentoringsystems.applicationname.model.db'' Contains models which will be stored in the database, naming Ojectname ''be.mentoringsystems.applicationname.presentation'' Contains controllers which will be accessed via Rest, naming ObjectnameController ''be.mentoringsystems.applicationname.security'' Contains overrides for security ''be.mentoringsystems.applicationname.service'' Contains Service interfaces, naming ObjectnameService ''be.mentoringsystems.applicationname.service.impl'' Contains Service implementations, naming ObjectnameServiceImpl, implements ObjectnameService, uses ObjectnameDAO & Transactional annotations ===== German Projects ===== ''de.jcpis.applicationname'' see above for details. ====== How to start : ====== Start with creating mocks & database model Create a new project : ''New Project -> Maven -> Web Application'' ===== POM : ===== Change the POM (configuration files -> ''pom.xml'') to this : com.msfcore MentoringSystems 1.2 4.0.0 be.mentoringsystems.applicationname applicationname war A description of the application 1.0-SNAPSHOT applicationname http://maven.apache.org javax.servlet servlet-api 2.5 provided javax.servlet.jsp jsp-api 2.1 provided org.springframework spring-aop 3.0.5.RELEASE org.springframework spring-asm 3.0.5.RELEASE org.springframework spring-aspects 3.0.5.RELEASE org.springframework spring-beans 3.0.5.RELEASE org.springframework spring-context 3.0.5.RELEASE org.springframework spring-context-support 3.0.5.RELEASE org.springframework spring-core 3.0.5.RELEASE commons-logging commons-logging org.springframework spring-expression 3.0.5.RELEASE org.springframework spring-orm 3.0.5.RELEASE org.springframework spring-oxm 3.0.5.RELEASE org.springframework spring-tx 3.0.5.RELEASE org.springframework spring-web 3.0.5.RELEASE org.springframework spring-webmvc 3.0.5.RELEASE org.springframework.security spring-security-acl 3.0.5.RELEASE org.springframework.security spring-security-aspects 3.0.5.RELEASE org.springframework.security spring-security-config 3.0.5.RELEASE org.springframework.security spring-security-core 3.0.5.RELEASE org.springframework.security spring-security-taglibs 3.0.5.RELEASE org.springframework.security spring-security-web 3.0.5.RELEASE org.codehaus.jackson jackson-core-lgpl 1.6.9 org.codehaus.jackson jackson-mapper-lgpl 1.6.9 javax.transaction jta 1.1 mysql mysql-connector-java 5.1.32 asm asm 3.3.1 asm asm-attrs 1.5.3 cglib cglib 2.2 org.hibernate jtidy r8-21122004 javax.activation activation 1.1.1 javax.annotation jsr250-api 1.0 javassist javassist 3.12.0.GA antlr antlr 2.7.6 org.hibernate hibernate-ehcache 3.6.10.Final org.hibernate hibernate-entitymanager 3.6.10.Final org.hibernate hibernate-tools 3.2.3.GA org.hibernate hibernate-commons-annotations 3.2.0.Final org.slf4j slf4j-api org.hibernate hibernate-core 3.6.10.Final org.slf4j slf4j-api commons-fileupload commons-fileupload 1.2.2 commons-codec commons-codec 1.8 commons-logging commons-logging 1.1.3 commons-pool commons-pool 1.5.5 commons-beanutils commons-beanutils 1.8.3 commons-logging commons-logging commons-beanutils commons-beanutils-bean-collections 1.8.3 commons-logging commons-logging commons-beanutils commons-beanutils-core 1.8.3 commons-logging commons-logging commons-collections commons-collections 3.1 commons-dbcp commons-dbcp 1.4 commons-io commons-io 2.0 commons-lang commons-lang 2.6 dom4j dom4j 1.6.1 log4j log4j 1.2.16 org.slf4j slf4j-api 1.6.1 org.slf4j slf4j-log4j12 1.6.1 net.bull.javamelody javamelody-core 1.52.0 org.apache.tomcat.maven tomcat6-maven-plugin 2.2 both org.apache.maven.plugins maven-dependency-plugin 2.8 org.apache.maven.plugins maven-resources-plugin 2.7 ${project.build.sourceEncoding} org.apache.maven.plugins maven-surefire-plugin 2.15 ${skipTests} -XX:-UseSplitVerifier This will set most of your dependencies straight, \\ the parent pom ensures all projects use UTF-8 encoding for files & sets some maven plugins. ===== web.xml : ===== Now we need the following in our ''WEB-INF/web.xml'' \\ Remove Spring Security if you are not using it! contextConfigLocation classpath:net/bull/javamelody/monitoring-spring.xml /WEB-INF/applicationname.xml net.bull.javamelody.SessionListener org.springframework.web.context.ContextLoaderListener org.springframework.web.util.Log4jConfigListener applicationname org.springframework.web.servlet.DispatcherServlet 2 applicationname / encodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 forceEncoding true encodingFilter / encodingFilter /* springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain /* monitoring net.bull.javamelody.MonitoringFilter url-exclude-pattern /static/.* monitoring /* 240 index.htm json application/json ===== applicationname.xml : ===== applicationname.xml is a spring context file which will bind most of the configuration files together \\ Here is a basic file for ''/WEB-INF/applicationname.xml'' seperate config files (services, security & database) now go into ''/WEB-INF/classes'', \\ you get them there by creating the following folder : ''/src/main/resources'' which in netbeans will translate to ''Other Sources/src/main/resources/'' Now that we have a skeleton, it's time to add some classes ====== Class buildup ====== ===== Model ===== Let's start with a model package be.mentoringsystems.applicationname.model.db; import java.io.Serializable; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.UUID; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.ManyToOne; import javax.persistence.Table; import javax.persistence.Temporal; import javax.persistence.TemporalType; import org.codehaus.jackson.annotate.JsonIgnoreProperties; import javax.persistence.OneToMany; import javax.persistence.Transient; import org.codehaus.jackson.map.annotate.JsonSerialize; import org.hibernate.annotations.Cascade; import org.hibernate.annotations.CascadeType; import org.hibernate.annotations.Type; // JPA: mark it as a database entity @Entity // JPA: which table @Table(name = "Login") // JSON: hide properties (hibernate properties have difficulty serializing, these properties were added automatically by hibernate byte code manipulation) @JsonIgnoreProperties({"hibernateLazyInitializer", "handler", "password"}) public class Login implements Serializable { private Integer id; private String login; private String password; private boolean admin = false; private boolean active = true; private String firstname; private String lastname; private String email; private Date createdOn = new Date(); private Date lastModified = new Date(); // JPA: marks date fields @Temporal(TemporalType.TIMESTAMP) // JPA: column definition, non-nullable fields require mentioning @Column(name = "createdOn", nullable = false, length = 19) // you can add serializers to always return the date formatted like dd.MM.yyyy //@JsonSerialize(using = DateTimeFormatter.class) public Date getCreatedOn() { return this.createdOn; } public void setCreatedOn(final Date createdOn) { this.createdOn = createdOn; } @Temporal(TemporalType.TIMESTAMP) @Column(name = "lastModified", length = 19) //@JsonSerialize(using = DateTimeFormatter.class) public Date getLastModified() { return this.lastModified; } public void setLastModified(final Date lastModified) { this.lastModified = lastModified; } /*@Column(name="rabat") public String getRabat() { return rabat; } public void setRabat(String rabat) { this.rabat = rabat; }*/ /*public LoginAttribut getLoginAttribut(final String key) { for(LoginAttribut attribut : this.getLoginAttributs()) { if(attribut.getParamKey().equals(key)) { return attribut; } } final LoginAttribut loginAttribut = new LoginAttribut(); loginAttribut.setParamKey(key); loginAttribut.setLogin(this); this.getLoginAttributs().add(loginAttribut); return loginAttribut; } // JPA: when this object is linked to multiple other objects, note fetchtype lazy. @OneToMany(fetch = FetchType.LAZY, mappedBy = "login") // Hibernate: This ensures the dependant records will be removed by hibernate before the delete query is executed. JPA Cascade doesn't work in this version. @Cascade(CascadeType.ALL) public List getLoginAttributs() { return loginAttributs; } public void setLoginAttributs(List loginAttributs) { this.loginAttributs = loginAttributs; }*/ public Login() { } // JPA mark ID @Id // JPA unique field @Column(name = "id", unique = true, nullable = false) // Hibernate Type & GeneratedValue for UUID (need to change the type of id to UUID) //@Type(type = "uuid-char") //@GeneratedValue(generator = "guid") // Hibernate GeneratedValue for mysql autoincrement columns INT(10) auto increment, Primary. @GeneratedValue(strategy = IDENTITY) public Integer getId() { return id; } public void setId(final Integer id) { this.id = id; } /* // JPA : when several of this object are linked to the other object @ManyToOne(fetch = FetchType.LAZY) // the column that defines the relation, usually contains the ID of other object @JoinColumn(name = "contextId", unique = true) public Context getContext() { return this.context; } public void setContext(Context context) { this.context = context; }*/ /* @Type(type = "uuid-char") // JPA : note usage of updatable & insertable, this allows us to have a column double so we can access the ID instead of the linked object. @Column(name = "contextId", updatable = false, insertable = false) public UUID getContextId() { return contextId; } public void setContextId(UUID contextId) { this.contextId = contextId; }*/ @Column(name = "login", nullable = false, length = 200) public String getLogin() { return this.login; } public void setLogin(String login) { this.login = login; } @Column(name = "password", nullable = false, length = 73) public String getPassword() { return this.password; } public void setPassword(String password) { this.password = password; } @Column(name = "admin", nullable = false, length = 1) // Hibernate type Char(1) values 'Y' or 'N' @Type(type = "yes_no") public boolean isAdmin() { return this.admin; } public void setAdmin(boolean admin) { this.admin = admin; } @Column(name = "active", nullable = false, length = 1) @Type(type = "yes_no") public boolean isActive() { return active; } public void setActive(boolean active) { this.active = active; } @Column(name = "firstname", nullable = false, length = 200) public String getFirstname() { return this.firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } @Column(name = "lastname", nullable = false, length = 200) public String getLastname() { return this.lastname; } public void setLastname(String lastname) { this.lastname = lastname; } @Column(name = "email", nullable = false) public String getEmail() { return this.email; } public void setEmail(String email) { this.email = email; } // transient value meaning this won't be saved in the database, in some implementations, it also means it won't be serialized. /*@Transient public boolean isLinkedToBerater() { return (this.getBerater() != null); }*/ } ===== DAO Interface ===== Now we could use a DAO interface package be.mentoringsystems.applicationname.dao; import be.mentoringsystems.applicationname.model.db.Login; import java.util.List; /** * * @author anthonyarents */ public interface LoginDAO { void saveLogin(final Login login); void deleteLogin(final Login login); Login getLoginById(final Integer loginId); Login getLoginByLogin(final String login); Login getLoginByLogin(final String loginName, final boolean active); Login getLoginByLoginWithoutReferences(final String login); Login getLoginByLoginWithoutReferences(final String login, final boolean active); Login getLoginByEmail(final String email); List getAllLogins(); List getAllLoginsBySearch(final String search, final int start, final int limit); Integer getAllLoginsCountBySearch(final String search); } ===== DAO Implementation ===== A DAO implementation package be.mentoringsystems.applicationname.dao.hibernate; import be.mentoringsystems.applicationname.dao.LoginDAO; import be.mentoringsystems.applicationname.model.db.Login; import java.util.List; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.MatchMode; import org.hibernate.criterion.Order; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.springframework.orm.hibernate3.HibernateTemplate; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; /** * * @author anthonyarents */ public final class HibernateLoginDAO extends HibernateDaoSupport implements LoginDAO { public void saveLogin(final Login login) { // save or update handles the checks to see if the object exists in the database getHibernateTemplate().saveOrUpdate(login); } public void deleteLogin(final Login login) { // normal delete, a "dummydelete" option is to add a delete flag to login & use save getHibernateTemplate().delete(login); } public Login getLoginById(final Integer loginId) { final HibernateTemplate template = getHibernateTemplate(); // Hibernate: select by Id Login login = template.get(Login.class, loginId); // Hibernate: reuse hibernatetemplate (good for performance) & initialize only the lazy collections you need //template.initialize(login.getLoginAttributs()); return login; } public Login getLoginByEmail(final String loginEmail) { final HibernateTemplate template = getHibernateTemplate(); // criteria queries final DetachedCriteria criteria = DetachedCriteria.forClass(Login.class); // Restrictions = where clause criteria.add(Restrictions.eq("email", loginEmail)); criteria.add(Restrictions.eq("active", true)); // Order (sorting) criteria.addOrder(Order.desc("createdOn")); final List result = template.findByCriteria(criteria); if(result.isEmpty()) { return null; } else { final Login login = result.get(0); //template.initialize(login.getBerater()); //template.initialize(login.getLoginAttributs()); //template.initialize(login.getContext()); return login; } } public Login getLoginByLoginWithoutReferences(final String loginName, final boolean active) { final HibernateTemplate template = getHibernateTemplate(); final DetachedCriteria criteria = DetachedCriteria.forClass(Login.class); criteria.add(Restrictions.eq("login", loginName)); if(active) { criteria.add(Restrictions.eq("active", active)); } criteria.addOrder(Order.desc("createdOn")); final List result = template.findByCriteria(criteria); if(result.isEmpty()) { return null; } else { final Login login = result.get(0); //template.initialize(login.getLoginAttributs()); //template.initialize(login.getContext()); //template.initialize(login.getContext().getContextAttributs()); return login; } } public Login getLoginByLoginWithoutReferences(final String loginName) { return getLoginByLoginWithoutReferences(loginName, true); } public Login getLoginByLogin(final String loginName, final boolean active) { final HibernateTemplate template = getHibernateTemplate(); final DetachedCriteria criteria = DetachedCriteria.forClass(Login.class); criteria.add(Restrictions.eq("login", loginName)); if(active) { criteria.add(Restrictions.eq("active", active)); } criteria.addOrder(Order.desc("createdOn")); final List result = template.findByCriteria(criteria); if(result.isEmpty()) { return null; } else { final Login login = result.get(0); //template.initialize(login.getBerater()); /*if(login.getBerater() != null) { template.initialize(login.getBerater().getBeratersForVorgesetzterId()); for(BeraterVorgesetzter beraterVorgesetzter : login.getBerater().getBeratersForBeraterId()) { template.initialize(beraterVorgesetzter.getBerater()); template.initialize(beraterVorgesetzter.getBerater().getEfpId()); } }*/ //template.initialize(login.getLoginAttributs()); //template.initialize(login.getContext()); //template.initialize(login.getContext().getContextAttributs()); return login; } } public Login getLoginByLogin(final String loginName) { return getLoginByLogin(loginName, true); } public List getAllLogins() { final HibernateTemplate template = getHibernateTemplate(); final List result = template.loadAll(Login.class); for(Login login : result) { /*if(login.getBerater() != null) { template.initialize(login.getBerater()); }*/ //template.initialize(login.getLoginAttributs()); //template.initialize(login.getContext()); } return result; } public Integer getAllLoginsCountBySearch(final String search) { final DetachedCriteria criteria = DetachedCriteria.forClass(Login.class); if(search != null) { criteria.add(Restrictions.or(Restrictions.like("login", "%" + search + "%"), Restrictions.like("lastname", "%" + search + "%"))); } criteria.setProjection(Projections.rowCount()); final List result = getHibernateTemplate().findByCriteria(criteria); return result.get(0).intValue(); } public List getAllLoginsBySearch(final String search, final int start, final int limit) { final HibernateTemplate template = getHibernateTemplate(); final DetachedCriteria criteria = DetachedCriteria.forClass(Login.class); criteria.addOrder(Order.asc("createdOn")); if(search != null) { criteria.add(Restrictions.or(Restrictions.like("login", "%" + search + "%"), Restrictions.like("lastname", "%" + search + "%"))); } final List result = template.findByCriteria(criteria, start, limit); for(Login login : result) { /*if(login.getBerater() != null) { template.initialize(login.getBerater()); } template.initialize(login.getLoginAttributs()); template.initialize(login.getContext());*/ } return result; } } Advanced hibernate criteria queries exist, If you need examples contact me :-). ===== Service Interface ===== package be.mentoringsystems.applicationname.service; import be.mentoringsystems.applicationname.model.db.Login; import java.util.List; /** * * @author anthonyarents */ public interface LoginService { void saveLogin(final Login login); void deleteLogin(final Login login); Login getLoginById(final Integer loginId); Login getLoginByLogin(final String login); Login getLoginByLogin(final String loginName, final boolean active); Login getLoginByLoginWithoutReferences(final String login); Login getLoginByLoginWithoutReferences(final String login, final boolean active); Login getLoginByEmail(final String email); List getAllLogins(); List getAllLoginsBySearch(final String search, final int start, final int limit); Integer getAllLoginsCountBySearch(final String search); } ===== Service Implementation ===== package be.mentoringsystems.applicationname.service.impl; import be.mentoringsystems.applicationname.dao.LoginDAO; import be.mentoringsystems.applicationname.model.db.Login; import be.mentoringsystems.applicationname.service.LoginService; import java.util.ArrayList; import java.util.List; // always use this logger import org.apache.log4j.Logger; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /** * * @author anthonyarents */ // Spring: using spring Transactional annotation to manage transactions, // supports = don't need to be in a transaction but can be part of one @Transactional(propagation = Propagation.SUPPORTS) public final class LoginServiceImpl implements LoginService { private transient LoginDAO loginDAO; private static final Logger LOGGER = Logger.getLogger(LoginServiceImpl.class); // required = need to be in a transaction, basicly, you want to change/delete/insert the object @Transactional(propagation = Propagation.REQUIRED) public void saveLogin(final Login login) { loginDAO.saveLogin(login); } @Transactional(propagation = Propagation.REQUIRED) public void deleteLogin(final Login login) { loginDAO.deleteLogin(login); } public Login getLoginById(final Integer loginId) { return loginDAO.getLoginById(loginId); } public Login getLoginByLogin(final String login) { return loginDAO.getLoginByLogin(login); } public Login getLoginByLoginWithoutReferences(final String login) { return loginDAO.getLoginByLoginWithoutReferences(login); } public Login getLoginByLogin(final String login, final boolean active) { return loginDAO.getLoginByLogin(login, active); } public Login getLoginByLoginWithoutReferences(final String login, final boolean active) { return loginDAO.getLoginByLoginWithoutReferences(login, active); } public Login getLoginByEmail(final String email) { return loginDAO.getLoginByEmail(email); } public List getAllLogins() { return loginDAO.getAllLogins(); } public List getAllLoginsBySearch(final String search, final int start, final int limit) { return loginDAO.getAllLoginsBySearch(search, start, limit); } public Integer getAllLoginsCountBySearch(final String search) { return loginDAO.getAllLoginsCountBySearch(search); } public void setLoginDAO(final LoginDAO loginDAO) { this.loginDAO = loginDAO; } } ===== Presentation (controllers) ===== Do not use DAO classes here! \\ Method signature : ''public ModelAndView methodname(HttpServletRequest hsr, HttpServlerResponse hsr1)'' \\ Use of MappingJacksonJsonView to return a json Object (mapping happens automatically). package be.mentoringsystems.applicationname.presentation; import be.mentoringsystems.applicationname.model.db.Login; import be.mentoringsystems.applicationname.service.LoginService; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.multiaction.MultiActionController; import org.springframework.web.servlet.view.json.MappingJacksonJsonView; /** * * @author anthonyarents */ public final class LoginController extends MultiActionController { private static final String SUCCESS = "success"; private transient LoginService loginService; private static final Logger LOGGER = Logger.getLogger(LoginController.class); public ModelAndView getLogin(final HttpServletRequest hsr, final HttpServletResponse hsr1) { final MappingJacksonJsonView view = new MappingJacksonJsonView(); view.addStaticAttribute(SUCCESS, false); final Login login = loginService.getLoginByLogin(hsr.getRemoteUser()); // use spring security in the future, hsr.getRemoteUser gives you the loginname but isn't always accurate if (login != null) { final Map userMap = new HashMap(); userMap.put("id", login.getId()); userMap.put("admin", login.isAdmin()); userMap.put("login", login.getLogin()); userMap.put("firstname", login.getFirstname()); userMap.put("lastname", login.getLastname()); userMap.put("email", login.getEmail()); view.addStaticAttribute("data", userMap); view.addStaticAttribute(SUCCESS, true); } return new ModelAndView(view); } public ModelAndView getAllLogins(final HttpServletRequest hsr, final HttpServletResponse hsr1) { final MappingJacksonJsonView view = new MappingJacksonJsonView(); view.addStaticAttribute(SUCCESS, false); //UUID contextId = HelperMethods.getUUID(hsr.getParameter("contextId")); //final Context context = contextService.getContextById(contextId); //if (securityService.hasAdminAccess(context)) { final List logins = loginService.getAllLogins(); view.addStaticAttribute(SUCCESS, true); view.addStaticAttribute("hits", logins.size()); view.addStaticAttribute("data", logins); //} return new ModelAndView(view); } public ModelAndView getAllLoginsBySearch(final HttpServletRequest hsr, final HttpServletResponse hsr1) { int start = 0; int limit = 20; if (hsr.getParameter("start") != null && hsr.getParameter("start").length() != 0) { start = Integer.parseInt(hsr.getParameter("start")); } if (hsr.getParameter("limit") != null && hsr.getParameter("limit").length() != 0) { limit = Integer.parseInt(hsr.getParameter("limit")); } final String search = hsr.getParameter("search"); //UUID contextId = HelperMethods.getUUID(hsr.getParameter("contextId")); //final Context context = contextService.getContextById(contextId); final MappingJacksonJsonView view = new MappingJacksonJsonView(); view.addStaticAttribute(SUCCESS, false); //if (securityService.hasAdminAccess(context)) { final List logins = loginService.getAllLoginsBySearch(search, start, limit); view.addStaticAttribute(SUCCESS, true); view.addStaticAttribute("hits", loginService.getAllLoginsCountBySearch(search)); view.addStaticAttribute("data", logins); //} return new ModelAndView(view); } public void setLoginService(final LoginService loginService) { this.loginService = loginService; } } ====== Further configuration ====== ===== applicationname-database.xml ===== add ''applicationname-database.xml'' in ''Other Sources/src/main/resources/'' be.mentoringsystems.applicationname.model.db be.mentoringsystems.applicationname.model.db false false false org.hibernate.dialect.MySQL5Dialect net.bull.javamelody.HibernateBatcherFactory true utf8 utf8 ===== applicationname-services.xml ===== add ''applicationname-services.xml'' in ''Other Sources/src/main/resources/'' ===== applicationname-servlet.xml ===== Add ''applicationname-servlet.xml'' to ''/WEB-INF/'' \\ This file is picked up by the dispatcher servlet of spring so is essentially in a different scope (it can access everything defined in spring context) loginController indexController ====== JSP Pages ====== ===== Login ===== Add ''login.jsp'' to ''/WEB-INF/jsp'' \\ Login

Login

You are free to change the implementation of the loginpage, \\ you'll find it easier to rely on an old fashioned form submit to login a user, the system will then redirect to app.html (app.jsp) ===== App ===== Add ''app.jsp'' to ''/WEB-INF/jsp'' \\ this should be the index.html of your extjs application ====== Static resources (javascript, css, ...) ====== Don'y use html pages, use jsp like above. simply place the resource in ''/static/'' or any subfolder there ====== Spring Security ====== Some overrides are needed to make things work the way we want them to : ===== Authentication provider ===== Spring Security needs some convincing to use our own models package be.mentoringsystems.applicationname.security; import be.mentoringsystems.applicationname.model.db.Login; import be.mentoringsystems.applicationname.service.LoginService; import java.util.ArrayList; import java.util.List; import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.User; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; /** * Implementation of AuthenticationProvider with support for our login service. * * @author anthonyarents */ @Transactional(propagation = Propagation.SUPPORTS) public final class CustomAuthenticationProvider implements AuthenticationProvider { private transient LoginService loginService; public void setLoginService(final LoginService loginService) { this.loginService = loginService; } /** * This method is executed by spring security, it will authenticate a user * (check if username & password are correct). * * @param authentication authentication object supplied by spring security * @return UsernamePasswordAuthenticationToken (if login worked) */ public Authentication authenticate(final Authentication authentication) final Login user = loginService.loginAsUser(authentication.getPrincipal().toString(), authentication.getCredentials().toString()); final List authorities = getAuthorities(user); // don't give the password, it would be unprotected ! final User userDetails = new User(authentication.getPrincipal().toString(), "", true, true, true, true, authorities); return new UsernamePasswordAuthenticationToken(userDetails, userDetails, authorities); } // This method can be used to add new roles based on login flags public List getAuthorities(final Login login) { final List authorities = new ArrayList(); if (login.isAdmin()) { authorities.add(new GrantedAuthorityImpl("ROLE_ADMIN")); } authorities.add(new GrantedAuthorityImpl("ROLE_USER")); return authorities; } /** * This method is used by spring security to determine if spring security * can use this class to authenticate a UsernamePasswordAuthenticationToken. * * @param type class of authenticationtoken * @return boolean */ public boolean supports(final Class type) { boolean result = false; if (type == UsernamePasswordAuthenticationToken.class) { result = true; } return result; } } ===== UserDetailsService ===== Spring Security doesn't use authentication provider for everything. package be.mentoringsystems.applicationname.security; import be.mentoringsystems.applicationname.model.db.Login; import be.mentoringsystems.applicationname.service.LoginService; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; import java.util.UUID; import org.apache.log4j.Logger; import org.springframework.dao.DataAccessException; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.core.authority.GrantedAuthorityImpl; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; /** * * @author anthonyarents */ public class UserDetailsServiceImpl implements UserDetailsService { private transient LoginService loginService; private static final Logger LOGGER = Logger.getLogger(UserDetailsServiceImpl.class); public void setLoginService(final LoginService loginService) { this.loginService = loginService; } @Override public UserDetails loadUserByUsername(String string) throws UsernameNotFoundException, DataAccessException { // get the user we want to switch to Login login = loginService.getLoginByLogin(string); // check if we are allowed to see the user (contexts!) /*Login admin = securityService.getLoginWithBerater(); if(admin.isAdmin() && !securityService.hasAdminAccess(admin, login.getContext())) { throw new AccessDeniedException("Admin " + admin.getLogin() + " is not allowed to log in as " + login.getLogin() + " due to context differences"); } Berater berater = beraterService.getBeraterById(login.getBeraterId()); if(!securityService.hasAccess(admin, berater)) { throw new AccessDeniedException("User " + admin.getLogin() + " is not allowed to log in as " + login.getLogin() + "."); }*/ return new User(login.getLogin(), login.getLogin(), true, true, true, true, getAuthorities(login)); } public List getAuthorities(final Login login) { final List authorities = new ArrayList(); if (login.isAdmin()) { authorities.add(new GrantedAuthorityImpl("ROLE_ADMIN")); } authorities.add(new GrantedAuthorityImpl("ROLE_USER")); return authorities; } } ===== Json fixes ===== We want a json message telling us we need to login when we do an AJAX request. else spring will redirect to the loginpage & our application will break without telling why. (not needed when polling the server) ==== Custom Access Denied Handler ==== package be.mentoringsystems.security; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandlerImpl; import org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper; /** * * @author anthonyarents */ public class CustomAccessDeniedHandler extends AccessDeniedHandlerImpl { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { final String jsonHeader = request.getHeader("X-Requested-With"); if (jsonHeader != null) { response.setStatus(SaveContextOnUpdateOrErrorResponseWrapper.SC_UNAUTHORIZED); response.setHeader("Location", null); response.setContentType("application/json;charset=UTF-8"); response.getOutputStream().print("{\"success\" : false, \"data\" : [], \"error\" : \"LOGIN REQUIRED\"}"); } else { super.handle(request, response, accessDeniedException); } } } ==== Custom Authentication Entry Point ==== package be.mentoringsystems.applicationname.security; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; import org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper; /** * * @author anthonyarents */ public class CustomAuthenticationEntryPoint extends LoginUrlAuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { final String jsonHeader = request.getHeader("X-Requested-With"); if (jsonHeader != null) { response.setStatus(SaveContextOnUpdateOrErrorResponseWrapper.SC_UNAUTHORIZED); response.setHeader("Location", null); response.setContentType("application/json;charset=UTF-8"); response.getOutputStream().print("{\"success\" : false, \"data\" : [], \"error\" : \"LOGIN REQUIRED\"}"); } else { super.commence(request, response, authException); } } } ===== applicationname-security.xml ===== add ''applicationname-security.xml'' in ''Other Sources/src/main/resources/''