diff --git a/navalplanner-business/pom.xml b/navalplanner-business/pom.xml
index d83513774..7dbb859d4 100644
--- a/navalplanner-business/pom.xml
+++ b/navalplanner-business/pom.xml
@@ -41,6 +41,14 @@
easymock
test
+
+ org.hibernate
+ hibernate-validator
+
+
+ org.hibernate
+ hibernate-annotations
+
diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Worker.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Worker.java
index 078ad2284..3946765ed 100644
--- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Worker.java
+++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/Worker.java
@@ -1,64 +1,69 @@
package org.navalplanner.business.resources.entities;
+import org.hibernate.validator.Min;
+import org.hibernate.validator.NotEmpty;
+
/**
* This class models a worker.
- *
* @author Fernando Bellas Permuy
- *
*/
public class Worker extends Resource {
-
+ @NotEmpty
private String firstName;
+
+ @NotEmpty
private String surname;
+
+ @NotEmpty
private String nif;
+
+ @Min(0)
private int dailyHours;
-
- public Worker() {}
-
- public Worker(String firstName, String surname, String nif,
- int dailyHours) {
-
+
+ public Worker() {
+ }
+
+ public Worker(String firstName, String surname, String nif, int dailyHours) {
this.firstName = firstName;
this.surname = surname;
this.nif = nif;
this.dailyHours = dailyHours;
-
}
-
+
public String getFirstName() {
return firstName;
}
-
+
public void setFirstName(String firstName) {
this.firstName = firstName;
}
-
+
public String getSurname() {
return surname;
}
-
+
public void setSurname(String surname) {
this.surname = surname;
- }
-
+ }
+
public String getNif() {
return nif;
}
-
+
public void setNif(String nif) {
this.nif = nif;
}
-
+
public int getDailyHours() {
return dailyHours;
}
-
+
public void setDailyHours(int dailyHours) {
this.dailyHours = dailyHours;
}
-
+
public int getDailyCapacity() {
return dailyHours;
}
-
+
}
diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/impl/ResourceServiceImpl.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/impl/ResourceServiceImpl.java
index 1db5bc9a0..45429a8cc 100644
--- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/impl/ResourceServiceImpl.java
+++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/services/impl/ResourceServiceImpl.java
@@ -15,9 +15,7 @@ import org.springframework.transaction.annotation.Transactional;
/**
* Implementation of the resource management service. Resource DAOs are
* autowired.
- *
* @author Fernando Bellas Permuy
- *
*/
@Transactional
public class ResourceServiceImpl implements ResourceService {
diff --git a/navalplanner-business/src/main/resources/navalplanner-business-hibernate.cfg.xml b/navalplanner-business/src/main/resources/navalplanner-business-hibernate.cfg.xml
index 00ed71ef5..32711b976 100644
--- a/navalplanner-business/src/main/resources/navalplanner-business-hibernate.cfg.xml
+++ b/navalplanner-business/src/main/resources/navalplanner-business-hibernate.cfg.xml
@@ -1,4 +1,4 @@
-
@@ -9,5 +9,11 @@
${hibernate.format_sql}
${hibernate.use_sql_comments}
${hibernate.hbm2ddl.auto}
+
+
+
+
+
+
diff --git a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/ResourceServiceTest.java b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/ResourceServiceTest.java
index df9ae69f2..38c0c3e8b 100644
--- a/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/ResourceServiceTest.java
+++ b/navalplanner-business/src/test/java/org/navalplanner/business/test/resources/services/ResourceServiceTest.java
@@ -1,5 +1,9 @@
package org.navalplanner.business.test.resources.services;
+import org.hibernate.SessionFactory;
+import org.hibernate.validator.ClassValidator;
+import org.hibernate.validator.InvalidStateException;
+import org.hibernate.validator.InvalidValue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException;
@@ -8,6 +12,7 @@ import org.navalplanner.business.resources.entities.ResourceGroup;
import org.navalplanner.business.resources.entities.Worker;
import org.navalplanner.business.resources.services.ResourceService;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.annotation.NotTransactional;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@@ -16,6 +21,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE;
import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE;
@@ -36,6 +42,9 @@ public class ResourceServiceTest {
@Autowired
private IResourceDao resourceDao;
+ @Autowired
+ private SessionFactory sessionFactory;
+
@Test
public void testAddResourceToResourceGroup()
throws InstanceNotFoundException {
@@ -194,4 +203,27 @@ public class ResourceServiceTest {
resourceService.getWorkers().size());
}
+ @Test
+ @NotTransactional
+ public void testWorkerValidation() throws Exception {
+ ClassValidator workerValidator = new ClassValidator(
+ Worker.class);
+ Worker[] invalidWorkers = {
+ new Worker("first name", null, "233233", 3),
+ new Worker("first name", "second name", "233233", -1),
+ new Worker(null, "second name", "233233", 3),
+ new Worker("first name", "second name", null, 3) };
+ for (Worker invalidWorker : invalidWorkers) {
+ InvalidValue[] invalidValues = workerValidator
+ .getInvalidValues(invalidWorker);
+ assertEquals(1, invalidValues.length);
+ try {
+ resourceService.saveResource(invalidWorker);
+ fail("must send invalid state exception");
+ } catch (InvalidStateException e) {
+ // ok
+ }
+ }
+ }
+
}
diff --git a/navalplanner-business/src/test/resources/navalplanner-business-hibernate-test.cfg.xml b/navalplanner-business/src/test/resources/navalplanner-business-hibernate-test.cfg.xml
index ab90ebbfd..59a91b345 100644
--- a/navalplanner-business/src/test/resources/navalplanner-business-hibernate-test.cfg.xml
+++ b/navalplanner-business/src/test/resources/navalplanner-business-hibernate-test.cfg.xml
@@ -1,7 +1,6 @@
-
-
${hibernate.dialect}
@@ -9,5 +8,11 @@
true
true
update
+
+
+
+
+
+
diff --git a/navalplanner-webapp/pom.xml b/navalplanner-webapp/pom.xml
index be920e184..6dcb12ba1 100644
--- a/navalplanner-webapp/pom.xml
+++ b/navalplanner-webapp/pom.xml
@@ -15,57 +15,61 @@
navalplanner-webapp
-
-
-
- org.springframework
- spring
-
-
-
- org.beanshell
- bsh
-
-
-
- commons-fileupload
- commons-fileupload
-
-
-
- org.zkoss.zk
- zul
-
-
- org.zkoss.zk
- zkplus
-
-
- org.zkoss.zk
- zk
-
-
-
- org.navalplanner
- navalplanner-gantt-zk
-
-
-
- org.navalplanner
- navalplanner-business
-
-
- org.easymock
- easymock
-
-
- junit
- junit
-
+
+
+
+ org.springframework
+ spring
+
+
+
+ org.beanshell
+ bsh
+
+
+
+ commons-fileupload
+ commons-fileupload
+
+
+
+ org.zkoss.zk
+ zul
+
+
+ org.zkoss.zk
+ zkplus
+
+
+ org.zkoss.zk
+ zk
+
+
+
+ org.navalplanner
+ navalplanner-gantt-zk
+
+
+
+ org.navalplanner
+ navalplanner-business
+
+
+ org.easymock
+ easymock
+
+
+ junit
+ junit
+
+
+ org.hibernate
+ hibernate-validator
+
javax.servlet
servlet-api
-
+
diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/IMessagesForUser.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/IMessagesForUser.java
new file mode 100644
index 000000000..11dd6389f
--- /dev/null
+++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/IMessagesForUser.java
@@ -0,0 +1,17 @@
+package org.navalplanner.web.common;
+
+import org.hibernate.validator.InvalidValue;
+
+/**
+ * Defines the ways in which information messages can be shown to the user
+ * @author Óscar González Fernández
+ */
+public interface IMessagesForUser {
+
+ void invalidValue(InvalidValue invalidValue);
+
+ void showMessage(Level level, String message);
+
+ void clearMessages();
+
+}
diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/Level.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/Level.java
new file mode 100644
index 000000000..6c5f612e0
--- /dev/null
+++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/Level.java
@@ -0,0 +1,5 @@
+package org.navalplanner.web.common;
+
+public enum Level {
+ INFO, WARNING, ERROR;
+}
\ No newline at end of file
diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/web/common/MessagesForUser.java b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/MessagesForUser.java
new file mode 100644
index 000000000..35d5def1d
--- /dev/null
+++ b/navalplanner-webapp/src/main/java/org/navalplanner/web/common/MessagesForUser.java
@@ -0,0 +1,102 @@
+package org.navalplanner.web.common;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+import org.hibernate.validator.InvalidValue;
+import org.zkoss.zk.ui.Component;
+import org.zkoss.zk.ui.event.Event;
+import org.zkoss.zk.ui.event.EventListener;
+import org.zkoss.zk.ui.event.Events;
+import org.zkoss.zk.ui.util.EventInterceptor;
+import org.zkoss.zk.ui.util.GenericForwardComposer;
+import org.zkoss.zul.Label;
+
+/**
+ * It shows messages to the user.
+ * @author Óscar González Fernández
+ */
+public class MessagesForUser extends GenericForwardComposer implements
+ IMessagesForUser {
+
+ private Component container;
+
+ private Queue pendingToDetach = new ConcurrentLinkedQueue();
+
+ private static final String DETACH_EVENT_NAME = "onMarkDetached";
+
+ public MessagesForUser(Component container) {
+ this.container = container;
+ container.getPage().getDesktop().addListener(new EventInterceptor() {
+
+ @Override
+ public void afterProcessEvent(Event event) {
+ }
+
+ @Override
+ public Event beforePostEvent(Event event) {
+ return event;
+ }
+
+ @Override
+ public Event beforeProcessEvent(Event event) {
+ if (event.getName().equals(DETACH_EVENT_NAME)
+ || pendingToDetach.isEmpty()) {
+ return event;
+ }
+ Component currrent = null;
+ while ((currrent = pendingToDetach.poll()) != null) {
+ currrent.detach();
+ }
+ return event;
+ }
+
+ @Override
+ public Event beforeSendEvent(Event event) {
+ return event;
+ }
+ });
+ }
+
+ @Override
+ public void invalidValue(InvalidValue invalidValue) {
+ addMessage(createLabelFor(invalidValue));
+ }
+
+ private Component createLabelFor(InvalidValue invalidValue) {
+ Label result = new Label();
+ result.setValue(invalidValue.getPropertyName() + ": "
+ + invalidValue.getMessage());
+ return result;
+ }
+
+ @Override
+ public void showMessage(Level level, String message) {
+ final Label label = new Label(message);
+ addMessage(label);
+ }
+
+ private void addMessage(final Component label) {
+ container.appendChild(label);
+ Events.echoEvent(DETACH_EVENT_NAME, label, "");
+ label.addEventListener(DETACH_EVENT_NAME, new EventListener() {
+
+ @Override
+ public void onEvent(Event event) throws Exception {
+ pendingToDetach.offer(label);
+ }
+ });
+ }
+
+ @Override
+ public void clearMessages() {
+ List