diff --git a/pom.xml b/pom.xml index 88db2ee2..2bb7e3ab 100644 --- a/pom.xml +++ b/pom.xml @@ -232,6 +232,12 @@ + + + com.googlecode.lambdaj + lambdaj + 2.2 + diff --git a/src/main/java/info/bukova/isspst/dao/BaseDao.java b/src/main/java/info/bukova/isspst/dao/BaseDao.java index de4f23bc..f5d68adf 100644 --- a/src/main/java/info/bukova/isspst/dao/BaseDao.java +++ b/src/main/java/info/bukova/isspst/dao/BaseDao.java @@ -13,6 +13,6 @@ public interface BaseDao { public List getAll(); public Query getQuery(String query); public List execQuery(String query); - public String getTableName(); + public String getEntityName(); } diff --git a/src/main/java/info/bukova/isspst/dao/IntegrityException.java b/src/main/java/info/bukova/isspst/dao/IntegrityException.java new file mode 100644 index 00000000..f45f6979 --- /dev/null +++ b/src/main/java/info/bukova/isspst/dao/IntegrityException.java @@ -0,0 +1,18 @@ +package info.bukova.isspst.dao; + +@SuppressWarnings("serial") +public class IntegrityException extends RuntimeException { + + public IntegrityException(String message) { + super( message ); + } + + public IntegrityException(Throwable root) { + super( root ); + } + + public IntegrityException(String message, Throwable root) { + super( message, root ); + } + +} diff --git a/src/main/java/info/bukova/isspst/dao/jpa/BaseDaoJPA.java b/src/main/java/info/bukova/isspst/dao/jpa/BaseDaoJPA.java index c6cae75b..8352f0f0 100644 --- a/src/main/java/info/bukova/isspst/dao/jpa/BaseDaoJPA.java +++ b/src/main/java/info/bukova/isspst/dao/jpa/BaseDaoJPA.java @@ -33,7 +33,8 @@ public abstract class BaseDaoJPA implements BaseDao { @SuppressWarnings("unchecked") @Override public T getById(int id) { - Query q = sessionFactory.getCurrentSession().createQuery("from " + getTableName() + " e where ID = :id"); + sessionFactory.getCurrentSession().enableFetchProfile("fetch-all"); + Query q = sessionFactory.getCurrentSession().createQuery("from " + getEntityName() + " e where ID = :id"); q.setInteger("id", id); return (T) q.uniqueResult(); } @@ -52,7 +53,7 @@ public abstract class BaseDaoJPA implements BaseDao { @SuppressWarnings("unchecked") @Override public List getAll() { - return sessionFactory.getCurrentSession().createQuery("from " + getTableName()).list(); + return sessionFactory.getCurrentSession().createQuery("from " + getEntityName()).list(); } } diff --git a/src/main/java/info/bukova/isspst/dao/jpa/RoleDaoJPA.java b/src/main/java/info/bukova/isspst/dao/jpa/RoleDaoJPA.java index d57d3da6..3fc92974 100644 --- a/src/main/java/info/bukova/isspst/dao/jpa/RoleDaoJPA.java +++ b/src/main/java/info/bukova/isspst/dao/jpa/RoleDaoJPA.java @@ -6,8 +6,8 @@ import info.bukova.isspst.data.Role; public class RoleDaoJPA extends BaseDaoJPA implements RoleDao { @Override - public String getTableName() { - return "ROLE"; + public String getEntityName() { + return "Role"; } } diff --git a/src/main/java/info/bukova/isspst/dao/jpa/UserDaoJPA.java b/src/main/java/info/bukova/isspst/dao/jpa/UserDaoJPA.java index 1db8d65f..b98028fd 100644 --- a/src/main/java/info/bukova/isspst/dao/jpa/UserDaoJPA.java +++ b/src/main/java/info/bukova/isspst/dao/jpa/UserDaoJPA.java @@ -7,8 +7,8 @@ public class UserDaoJPA extends BaseDaoJPA implements UserDao { @Override - public String getTableName() { - return "USER"; + public String getEntityName() { + return "User"; } } diff --git a/src/main/java/info/bukova/isspst/data/DataModel.java b/src/main/java/info/bukova/isspst/data/DataModel.java index 70254945..2634d38f 100644 --- a/src/main/java/info/bukova/isspst/data/DataModel.java +++ b/src/main/java/info/bukova/isspst/data/DataModel.java @@ -10,5 +10,7 @@ public interface DataModel { public Date getCreated(); public void setModified(Date modified); public Date getModified(); + public boolean isValid(); + public void setValid(boolean valid); } diff --git a/src/main/java/info/bukova/isspst/data/Role.java b/src/main/java/info/bukova/isspst/data/Role.java index bad18950..6ecc2710 100644 --- a/src/main/java/info/bukova/isspst/data/Role.java +++ b/src/main/java/info/bukova/isspst/data/Role.java @@ -7,6 +7,7 @@ import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.Table; +import javax.persistence.Transient; import org.springframework.security.core.GrantedAuthority; @@ -23,7 +24,7 @@ public class Role implements GrantedAuthority, DataModel { @Column(name="ID") @GeneratedValue private int id; - @Column(name="AUTHORITY") + @Column(name="AUTHORITY", unique=true) private String authority; @Column(name="DESCRIPTION") private String description; @@ -31,6 +32,8 @@ public class Role implements GrantedAuthority, DataModel { private Date created; @Column(name="MODIFIED") private Date modified; + @Transient + private boolean valid; @Override public String getAuthority() { @@ -73,4 +76,12 @@ public class Role implements GrantedAuthority, DataModel { this.modified = modified; } + public boolean isValid() { + return valid; + } + + public void setValid(boolean valid) { + this.valid = valid; + } + } diff --git a/src/main/java/info/bukova/isspst/data/User.java b/src/main/java/info/bukova/isspst/data/User.java index fbedd064..9f5f0c4b 100644 --- a/src/main/java/info/bukova/isspst/data/User.java +++ b/src/main/java/info/bukova/isspst/data/User.java @@ -1,16 +1,19 @@ package info.bukova.isspst.data; +import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.persistence.Column; import javax.persistence.Entity; +import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinTable; import javax.persistence.MapKeyColumn; import javax.persistence.OneToMany; import javax.persistence.Table; +import javax.persistence.Transient; import org.springframework.security.core.userdetails.UserDetails; @@ -27,19 +30,25 @@ public class User implements UserDetails, DataModel { @Column(name="ID") @GeneratedValue private int id; - @Column(name="USERNAME") + @Column(name="USERNAME", unique=true) private String username; @Column(name="PASSWORD") private String password; @Column(name="ENABLED") private boolean enabled; - @OneToMany @JoinTable(name="USER_ROLE") + @OneToMany(fetch=FetchType.EAGER) @JoinTable(name="USER_ROLE") @MapKeyColumn(name="ROLE_ID") private List authorities; @Column(name="CREATED") private Date created; @Column(name="MODIFIED") private Date modified; + @Transient + private boolean valid; + + public User() { + authorities = new ArrayList(); + } @Override public List getAuthorities() { @@ -120,4 +129,12 @@ public class User implements UserDetails, DataModel { this.modified = modified; } + public boolean isValid() { + return valid; + } + + public void setValid(boolean valid) { + this.valid = valid; + } + } diff --git a/src/main/java/info/bukova/isspst/filters/Filter.java b/src/main/java/info/bukova/isspst/filters/Filter.java new file mode 100644 index 00000000..9c206f58 --- /dev/null +++ b/src/main/java/info/bukova/isspst/filters/Filter.java @@ -0,0 +1,10 @@ +package info.bukova.isspst.filters; + +import org.hamcrest.TypeSafeMatcher; + +public interface Filter { + + public TypeSafeMatcher matcher(); + public String queryString(); + +} diff --git a/src/main/java/info/bukova/isspst/services/AbstractService.java b/src/main/java/info/bukova/isspst/services/AbstractService.java index 85caf381..163873f6 100644 --- a/src/main/java/info/bukova/isspst/services/AbstractService.java +++ b/src/main/java/info/bukova/isspst/services/AbstractService.java @@ -1,16 +1,28 @@ package info.bukova.isspst.services; +import static ch.lambdaj.Lambda.filter; + import java.util.Date; import java.util.List; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validator; +import org.hibernate.HibernateException; +import org.hibernate.NonUniqueResultException; +import org.hibernate.Query; import org.springframework.transaction.annotation.Transactional; import info.bukova.isspst.dao.BaseDao; +import info.bukova.isspst.dao.IntegrityException; import info.bukova.isspst.data.DataModel; +import info.bukova.isspst.filters.Filter; public abstract class AbstractService implements Service { protected BaseDao dao; + private Validator validator; public void setDao(BaseDao dao) { this.dao = dao; @@ -33,22 +45,73 @@ public abstract class AbstractService implements Service @Override @Transactional public void delete(T entity) { - dao.delete(entity); + try { + dao.delete(entity); + } catch (HibernateException e) { + throw new IntegrityException(e); + } + } + + @Override + public void validate(T entity) { + if (validator == null) { + entity.setValid(true); + return; + } + + Set> violations = validator.validate(entity); + + if (!violations.isEmpty()) { + ValidationException ex = new ValidationException(); + for(ConstraintViolation v : violations) { + String errKye = v.getPropertyPath().toString(); + ex.addMessage(errKye.isEmpty() ? "CLASS_ERR" : errKye, v.getMessage()); + } + + throw ex; + } + + entity.setValid(true); + } + + @Override + public List filterList(List sourceList, Filter filter) { + return filter(filter.matcher(), sourceList); } @Override + @Transactional + public T getById(int id) { + + return dao.getById(id); + } + + @Override + @Transactional public List getAll() { return dao.getAll(); } @Override + @Transactional public List execQuery(String query) { return dao.execQuery(query); } + @SuppressWarnings("unchecked") @Override + @Transactional public T selectSingle(String query) { - return null; + try { + Query q = dao.getQuery(query); + return (T) q.uniqueResult(); + } catch (NonUniqueResultException e) { + return null; + } + } + + public void setValidator(Validator validator) { + this.validator = validator; } } diff --git a/src/main/java/info/bukova/isspst/services/RoleService.java b/src/main/java/info/bukova/isspst/services/RoleService.java index 4171ec00..f85e27ca 100644 --- a/src/main/java/info/bukova/isspst/services/RoleService.java +++ b/src/main/java/info/bukova/isspst/services/RoleService.java @@ -4,4 +4,6 @@ import info.bukova.isspst.data.Role; public interface RoleService extends Service { + public Role getRoleByAuthority(String authority); + } diff --git a/src/main/java/info/bukova/isspst/services/RoleServiceImpl.java b/src/main/java/info/bukova/isspst/services/RoleServiceImpl.java index 8d8e4c46..0fd81feb 100644 --- a/src/main/java/info/bukova/isspst/services/RoleServiceImpl.java +++ b/src/main/java/info/bukova/isspst/services/RoleServiceImpl.java @@ -1,7 +1,15 @@ package info.bukova.isspst.services; +import org.springframework.transaction.annotation.Transactional; + import info.bukova.isspst.data.Role; public class RoleServiceImpl extends AbstractService implements RoleService { + @Override + @Transactional + public Role getRoleByAuthority(String authority) { + return this.selectSingle("from Role where authority = '" + authority + "'"); + } + } diff --git a/src/main/java/info/bukova/isspst/services/Service.java b/src/main/java/info/bukova/isspst/services/Service.java index 54d7097c..bf3ab850 100644 --- a/src/main/java/info/bukova/isspst/services/Service.java +++ b/src/main/java/info/bukova/isspst/services/Service.java @@ -1,5 +1,7 @@ package info.bukova.isspst.services; +import info.bukova.isspst.filters.Filter; + import java.util.List; public interface Service { @@ -7,9 +9,11 @@ public interface Service { public void add(T entity); public void update(T entity); public void delete(T entity); + public void validate(T entity) throws ValidationException; public List getAll(); public List execQuery(String query); public T selectSingle(String query); - + public T getById(int id); + public List filterList(List sourceList, Filter filter); } diff --git a/src/main/java/info/bukova/isspst/services/UserService.java b/src/main/java/info/bukova/isspst/services/UserService.java index 6cff5997..de8af3b7 100644 --- a/src/main/java/info/bukova/isspst/services/UserService.java +++ b/src/main/java/info/bukova/isspst/services/UserService.java @@ -5,5 +5,7 @@ import info.bukova.isspst.data.User; public interface UserService extends Service { public void setPassword(User user, String password); + public boolean hasRole(User user, String authority); + public void test(); } diff --git a/src/main/java/info/bukova/isspst/services/UserServiceImpl.java b/src/main/java/info/bukova/isspst/services/UserServiceImpl.java index 7f4e5f8a..af5a3c75 100644 --- a/src/main/java/info/bukova/isspst/services/UserServiceImpl.java +++ b/src/main/java/info/bukova/isspst/services/UserServiceImpl.java @@ -1,12 +1,15 @@ package info.bukova.isspst.services; import org.hibernate.Query; +import org.springframework.security.access.annotation.Secured; import org.springframework.security.authentication.encoding.PasswordEncoder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.transaction.annotation.Transactional; +import info.bukova.isspst.Constants; +import info.bukova.isspst.data.Role; import info.bukova.isspst.data.User; public class UserServiceImpl extends AbstractService implements UserService, UserDetailsService { @@ -37,5 +40,21 @@ public class UserServiceImpl extends AbstractService implements UserServic user.setPassword(encoder.encodePassword(password, user.getUsername())); } + @Override + public boolean hasRole(User user, String authority) { + for (Role r : user.getAuthorities()) { + if (r.getAuthority().equals(authority)) { + return true; + } + } + return false; + } + + @Override + @Secured(Constants.ROLE_ADMIN) + public void test() { + System.out.println("pokus secured"); + } + } diff --git a/src/main/java/info/bukova/isspst/services/ValidationException.java b/src/main/java/info/bukova/isspst/services/ValidationException.java new file mode 100644 index 00000000..467e21f2 --- /dev/null +++ b/src/main/java/info/bukova/isspst/services/ValidationException.java @@ -0,0 +1,28 @@ +package info.bukova.isspst.services; + +import java.util.HashMap; +import java.util.Map; + +public class ValidationException extends RuntimeException { + + private static final long serialVersionUID = -2251676786418660137L; + + private Map messages; + + public ValidationException() { + messages = new HashMap(); + } + + public void addMessage(String property, String message) { + messages.put(property, message); + } + + public Map getMessages() { + return messages; + } + + public void setMessages(Map messages) { + this.messages = messages; + } + +} diff --git a/src/main/java/info/bukova/isspst/ui/FormViewModel.java b/src/main/java/info/bukova/isspst/ui/FormViewModel.java new file mode 100644 index 00000000..48e01e56 --- /dev/null +++ b/src/main/java/info/bukova/isspst/ui/FormViewModel.java @@ -0,0 +1,61 @@ +package info.bukova.isspst.ui; + +import info.bukova.isspst.dao.IntegrityException; +import info.bukova.isspst.services.Service; +import info.bukova.isspst.services.ValidationException; + +import java.util.Map; + +import org.zkoss.bind.annotation.BindingParam; +import org.zkoss.bind.annotation.Command; +import org.zkoss.bind.annotation.ExecutionArgParam; +import org.zkoss.bind.annotation.Init; +import org.zkoss.bind.annotation.NotifyChange; +import org.zkoss.zul.Messagebox; +import org.zkoss.zul.Window; + +public class FormViewModel { + + private T dataBean; + private Map errMessages; + private Service service; + + @Init + public void init(@ExecutionArgParam("selected") T selected, @ExecutionArgParam("service") Service service) { + this.dataBean = selected; + this.service = service; + } + + public T getDataBean() { + return dataBean; + } + + @Command + @NotifyChange("errMessages") + public void save(@BindingParam("window") Window win) { + try { + service.update(dataBean); + win.detach(); + } catch (ValidationException e) { + errMessages = e.getMessages(); + String classErr = errMessages.get("CLASS_ERR"); + + if (classErr == null) { + classErr = ""; + } + + Messagebox.show("Chyba validace", "Error", Messagebox.OK, Messagebox.ERROR); + } catch (IntegrityException e) { + e.printStackTrace(); + } + } + + public Map getErrMessages() { + return errMessages; + } + + protected void setErrMesages(Map msgs) { + this.errMessages = msgs; + } + +} diff --git a/src/main/java/info/bukova/isspst/ui/ListViewModel.java b/src/main/java/info/bukova/isspst/ui/ListViewModel.java new file mode 100644 index 00000000..1842cd89 --- /dev/null +++ b/src/main/java/info/bukova/isspst/ui/ListViewModel.java @@ -0,0 +1,263 @@ +package info.bukova.isspst.ui; + +import info.bukova.isspst.dao.IntegrityException; +import info.bukova.isspst.data.DataModel; +import info.bukova.isspst.filters.Filter; +import info.bukova.isspst.services.Service; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.zkoss.bind.annotation.BindingParam; +import org.zkoss.bind.annotation.Command; +import org.zkoss.bind.annotation.GlobalCommand; +import org.zkoss.bind.annotation.NotifyChange; +import org.zkoss.zk.ui.Executions; +import org.zkoss.zul.Messagebox; +import org.zkoss.zul.Window; + +public class ListViewModel { + + private boolean confirmDelete = false; + private boolean filter = false; + private Window editWin; + private T dataBean; + //private T filterTemplate; + private Filter dataFilter; + private T editBean; + private List dataList; + private List fullList; + private List tmpList; + private int selIndex = -1; + private String sortCol; + private boolean sortDesc = true; + private boolean newRec = false; + private boolean fullFill = false; + + protected Service service; + protected Class dataClass; + protected String formZul; + + public List getDataList() { + if (dataList == null) { + dataList = new ArrayList(); + loadFromDb(); + } + + return dataList; + } + + public void setDataBean(T data) { + this.dataBean = data; + } + + public void setDataFilter(Filter dataFilter) { + this.dataFilter = dataFilter; + } + + public T getDataBean() { + return dataBean; + } + + public boolean getConfirmDelete() { + return confirmDelete; + } + + public boolean getFilter() { + return this.filter; + } + + public boolean getFullFill() { + return fullFill; + } + + protected void newRecMode() { + newRec = true; + } + + protected void setEditBean(T edit) { + this.editBean = edit; + } + + + @Command + @NotifyChange({"filter", "dataList", "dataBean"}) + public void filter() { + filter = !filter; + + if (!filter) { + dataList = fullList; + dataBean = null; + selIndex = -1; + } else { + doFilter(); + dataBean = null; + } + } + + @Command + @NotifyChange("dataList") + public void doFilter() { + if (dataFilter == null) { + return; + } + + List result = service.filterList(fullList, dataFilter); + selIndex = -1; + dataList = result; + } + + @Command + public void addNew() { + try { + newRecMode(); + editBean = dataClass.newInstance(); + showForm(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + @Command + public void edit() { + int index = dataList.indexOf(dataBean); + if (index != -1) { + selIndex = index; + } + editBean = service.getById(dataBean.getId()); + showForm(); + } + + @Command + @NotifyChange("confirmDelete") + public void delObject() { + confirmDelete = true; + } + + @Command + @NotifyChange({"dataList", "dataBean"}) + public void delete() { + try { + service.delete(dataBean); + dataList.remove(dataBean); + dataBean = null; + } catch (IntegrityException e) { + Messagebox.show("Error while deleting object", "Error", Messagebox.OK, Messagebox.ERROR); + } + confirmDelete = false; + } + + @GlobalCommand + @NotifyChange({"dataList", "dataBean"}) + public void refresh() { + if (editBean != null && !editBean.isValid()) { + return; + } + if (!filter && newRec) { + dataList.add(editBean); + if (dataList != fullList) { + fullList.add(editBean); + } + } else if (newRec) { + fullList.add(editBean); + } + dataBean = editBean; + if (!newRec) { + dataList.set(selIndex, editBean); + } + } + + @GlobalCommand + @NotifyChange({"dataList", "dataBean", "fullFill"}) + public void reload() { + dataBean = null; + dataList.clear(); + loadFromDbSync(); + } + + @Command + @NotifyChange("selIndex") + public void afterRender() { + if (editBean != null && !editBean.isValid()) { + return; + } + + if (selIndex > dataList.size() -1) { + selIndex = -1; + } + + if (newRec) { + selIndex = dataList.size() -1; + newRec = false; + } + } + + @Command + @NotifyChange("dataBean") + public void onSort(@BindingParam("column") String column) { + if (sortCol == null || this.sortCol.equals(column)) + this.sortDesc = !this.sortDesc; + + this.sortCol = column; + selIndex = -1; + dataBean = null; + } + + public int getSelIndex() { + return this.selIndex; + } + + @Command + @NotifyChange({"dataList", "fullFill"}) + public void fullFill() { + if (fullFill && dataList.isEmpty()) { + dataList.addAll(tmpList); + fullList = dataList; + } + } + + private void loadFromDb() { + Thread fillThread = new Thread(new Runnable() { + + @Override + public void run() { + tmpList = service.getAll(); + + try { + Thread.sleep(200); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + fullFill = true; + } + }); + + fillThread.start(); + } + + private void loadFromDbSync() { + dataList.addAll(service.getAll()); + } + + protected void showForm() { + Map arg = new HashMap(); + arg.put("selected", editBean); + arg.put("service", service); + editWin = (Window) Executions.createComponents(formZul, null, arg); + editWin.doModal(); + } + + protected List getFullList() { + return fullList; + } + + protected void setFullFill(boolean fullFill) { + this.fullFill = fullFill; + } + +} diff --git a/src/main/java/info/bukova/isspst/ui/TestVM.java b/src/main/java/info/bukova/isspst/ui/TestVM.java new file mode 100644 index 00000000..b516c080 --- /dev/null +++ b/src/main/java/info/bukova/isspst/ui/TestVM.java @@ -0,0 +1,18 @@ +package info.bukova.isspst.ui; + +import org.zkoss.bind.annotation.Command; +import org.zkoss.zk.ui.select.annotation.WireVariable; + +import info.bukova.isspst.services.UserService; + +public class TestVM { + + @WireVariable + private UserService userService; + + @Command + public void test() { + userService.test(); + } + +} diff --git a/src/main/webapp/WEB-INF/spring/root-context.xml b/src/main/webapp/WEB-INF/spring/root-context.xml index abab89a5..ca5f890c 100644 --- a/src/main/webapp/WEB-INF/spring/root-context.xml +++ b/src/main/webapp/WEB-INF/spring/root-context.xml @@ -38,7 +38,7 @@ ${jdbc.dialect} true - create + update @@ -50,10 +50,13 @@ + + - - + + @@ -73,6 +76,8 @@ + + @@ -86,6 +91,11 @@ + + + + + diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml index 767cbba3..45682012 100644 --- a/src/main/webapp/WEB-INF/web.xml +++ b/src/main/webapp/WEB-INF/web.xml @@ -18,6 +18,10 @@ org.springframework.web.context.ContextLoaderListener + + info.bukova.isspst.DbInitListener + + ZK listener for session cleanup @@ -109,7 +113,7 @@ appServlet - / + /api/ diff --git a/src/main/webapp/admin/index.zhtml b/src/main/webapp/admin/index.zhtml new file mode 100644 index 00000000..be965415 --- /dev/null +++ b/src/main/webapp/admin/index.zhtml @@ -0,0 +1,11 @@ + + + + + +Objednávky + + + administrace + + \ No newline at end of file diff --git a/src/main/webapp/admin/test.zul b/src/main/webapp/admin/test.zul new file mode 100644 index 00000000..3c408ca0 --- /dev/null +++ b/src/main/webapp/admin/test.zul @@ -0,0 +1,10 @@ + + + + +