ItEr08S06ArquitecturaClientesItEr07S07: Hibernate validations dependences added. Annotating Worker with. Add facility to show messages to the user, integrated with WorkerCrudController.

This commit is contained in:
Óscar González Fernández 2009-05-14 17:16:01 +02:00 committed by Javier Moran Rua
parent 4924acc8b9
commit fd9e9feba9
14 changed files with 728 additions and 463 deletions

View file

@ -41,6 +41,14 @@
<artifactId>easymock</artifactId> <artifactId>easymock</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-annotations</artifactId>
</dependency>
</dependencies> </dependencies>
</project> </project>

View file

@ -1,28 +1,33 @@
package org.navalplanner.business.resources.entities; package org.navalplanner.business.resources.entities;
import org.hibernate.validator.Min;
import org.hibernate.validator.NotEmpty;
/** /**
* This class models a worker. * This class models a worker.
*
* @author Fernando Bellas Permuy <fbellas@udc.es> * @author Fernando Bellas Permuy <fbellas@udc.es>
*
*/ */
public class Worker extends Resource { public class Worker extends Resource {
@NotEmpty
private String firstName; private String firstName;
@NotEmpty
private String surname; private String surname;
@NotEmpty
private String nif; private String nif;
@Min(0)
private int dailyHours; private int dailyHours;
public Worker() {} public Worker() {
}
public Worker(String firstName, String surname, String nif,
int dailyHours) {
public Worker(String firstName, String surname, String nif, int dailyHours) {
this.firstName = firstName; this.firstName = firstName;
this.surname = surname; this.surname = surname;
this.nif = nif; this.nif = nif;
this.dailyHours = dailyHours; this.dailyHours = dailyHours;
} }
public String getFirstName() { public String getFirstName() {

View file

@ -15,9 +15,7 @@ import org.springframework.transaction.annotation.Transactional;
/** /**
* Implementation of the resource management service. Resource DAOs are * Implementation of the resource management service. Resource DAOs are
* autowired. * autowired.
*
* @author Fernando Bellas Permuy <fbellas@udc.es> * @author Fernando Bellas Permuy <fbellas@udc.es>
*
*/ */
@Transactional @Transactional
public class ResourceServiceImpl implements ResourceService { public class ResourceServiceImpl implements ResourceService {

View file

@ -9,5 +9,11 @@
<property name="hibernate.format_sql">${hibernate.format_sql}</property> <property name="hibernate.format_sql">${hibernate.format_sql}</property>
<property name="hibernate.use_sql_comments">${hibernate.use_sql_comments}</property> <property name="hibernate.use_sql_comments">${hibernate.use_sql_comments}</property>
<property name="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</property> <property name="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</property>
<event type="pre-update">
<listener class="org.hibernate.validator.event.ValidateEventListener" />
</event>
<event type="pre-insert">
<listener class="org.hibernate.validator.event.ValidateEventListener" />
</event>
</session-factory> </session-factory>
</hibernate-configuration> </hibernate-configuration>

View file

@ -1,5 +1,9 @@
package org.navalplanner.business.test.resources.services; 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.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.navalplanner.business.common.exceptions.InstanceNotFoundException; 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.entities.Worker;
import org.navalplanner.business.resources.services.ResourceService; import org.navalplanner.business.resources.services.ResourceService;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.NotTransactional;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional; 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.assertFalse;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; 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.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_FILE;
import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE; import static org.navalplanner.business.test.BusinessGlobalNames.BUSINESS_SPRING_CONFIG_TEST_FILE;
@ -36,6 +42,9 @@ public class ResourceServiceTest {
@Autowired @Autowired
private IResourceDao resourceDao; private IResourceDao resourceDao;
@Autowired
private SessionFactory sessionFactory;
@Test @Test
public void testAddResourceToResourceGroup() public void testAddResourceToResourceGroup()
throws InstanceNotFoundException { throws InstanceNotFoundException {
@ -194,4 +203,27 @@ public class ResourceServiceTest {
resourceService.getWorkers().size()); resourceService.getWorkers().size());
} }
@Test
@NotTransactional
public void testWorkerValidation() throws Exception {
ClassValidator<Worker> workerValidator = new ClassValidator<Worker>(
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
}
}
}
} }

View file

@ -1,7 +1,6 @@
<!DOCTYPE hibernate-configuration <!DOCTYPE hibernate-configuration
PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration> <hibernate-configuration>
<session-factory> <session-factory>
<property name="hibernate.dialect">${hibernate.dialect}</property> <property name="hibernate.dialect">${hibernate.dialect}</property>
@ -9,5 +8,11 @@
<property name="hibernate.format_sql">true</property> <property name="hibernate.format_sql">true</property>
<property name="hibernate.use_sql_comments">true</property> <property name="hibernate.use_sql_comments">true</property>
<property name="hibernate.hbm2ddl.auto">update</property> <property name="hibernate.hbm2ddl.auto">update</property>
<event type="pre-update">
<listener class="org.hibernate.validator.event.ValidateEventListener" />
</event>
<event type="pre-insert">
<listener class="org.hibernate.validator.event.ValidateEventListener" />
</event>
</session-factory> </session-factory>
</hibernate-configuration> </hibernate-configuration>

View file

@ -15,57 +15,61 @@
<finalName>navalplanner-webapp</finalName> <finalName>navalplanner-webapp</finalName>
</build> </build>
<dependencies> <dependencies>
<!-- Spring --> <!-- Spring -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring</artifactId> <artifactId>spring</artifactId>
</dependency> </dependency>
<!-- BeanShell (required by ZK) --> <!-- BeanShell (required by ZK) -->
<dependency> <dependency>
<groupId>org.beanshell</groupId> <groupId>org.beanshell</groupId>
<artifactId>bsh</artifactId> <artifactId>bsh</artifactId>
</dependency> </dependency>
<!-- Apache Commons Fileupload (required by ZK) --> <!-- Apache Commons Fileupload (required by ZK) -->
<dependency> <dependency>
<groupId>commons-fileupload</groupId> <groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId> <artifactId>commons-fileupload</artifactId>
</dependency> </dependency>
<!-- ZK --> <!-- ZK -->
<dependency> <dependency>
<groupId>org.zkoss.zk</groupId> <groupId>org.zkoss.zk</groupId>
<artifactId>zul</artifactId> <artifactId>zul</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.zkoss.zk</groupId> <groupId>org.zkoss.zk</groupId>
<artifactId>zkplus</artifactId> <artifactId>zkplus</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.zkoss.zk</groupId> <groupId>org.zkoss.zk</groupId>
<artifactId>zk</artifactId> <artifactId>zk</artifactId>
</dependency> </dependency>
<!-- Naval Planner ZK Components --> <!-- Naval Planner ZK Components -->
<dependency> <dependency>
<groupId>org.navalplanner</groupId> <groupId>org.navalplanner</groupId>
<artifactId>navalplanner-gantt-zk</artifactId> <artifactId>navalplanner-gantt-zk</artifactId>
</dependency> </dependency>
<!-- Naval Planner Business --> <!-- Naval Planner Business -->
<dependency> <dependency>
<groupId>org.navalplanner</groupId> <groupId>org.navalplanner</groupId>
<artifactId>navalplanner-business</artifactId> <artifactId>navalplanner-business</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.easymock</groupId> <groupId>org.easymock</groupId>
<artifactId>easymock</artifactId> <artifactId>easymock</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId> <artifactId>servlet-api</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View file

@ -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 <br />
* @author Óscar González Fernández <ogonzalez@igalia.com>
*/
public interface IMessagesForUser {
void invalidValue(InvalidValue invalidValue);
void showMessage(Level level, String message);
void clearMessages();
}

View file

@ -0,0 +1,5 @@
package org.navalplanner.web.common;
public enum Level {
INFO, WARNING, ERROR;
}

View file

@ -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. <br />
* @author Óscar González Fernández <ogonzalez@igalia.com>
*/
public class MessagesForUser extends GenericForwardComposer implements
IMessagesForUser {
private Component container;
private Queue<Component> pendingToDetach = new ConcurrentLinkedQueue<Component>();
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<Object> children = new ArrayList<Object>(container.getChildren());
for (Object child : children) {
Component c = (Component) child;
c.detach();
}
}
}

View file

@ -4,7 +4,12 @@ import java.util.List;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.hibernate.validator.ClassValidator;
import org.hibernate.validator.InvalidValue;
import org.navalplanner.business.resources.entities.Worker; import org.navalplanner.business.resources.entities.Worker;
import org.navalplanner.web.common.IMessagesForUser;
import org.navalplanner.web.common.Level;
import org.navalplanner.web.common.MessagesForUser;
import org.navalplanner.web.common.OnlyOneVisible; import org.navalplanner.web.common.OnlyOneVisible;
import org.navalplanner.web.common.Util; import org.navalplanner.web.common.Util;
import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Component;
@ -20,6 +25,9 @@ public class WorkerCRUDController extends GenericForwardComposer {
private static final Log LOG = LogFactory private static final Log LOG = LogFactory
.getLog(WorkerCRUDController.class); .getLog(WorkerCRUDController.class);
private ClassValidator<Worker> workerValidator = new ClassValidator<Worker>(
Worker.class);
private Window createWindow; private Window createWindow;
private Window listWindow; private Window listWindow;
@ -32,16 +40,21 @@ public class WorkerCRUDController extends GenericForwardComposer {
private OnlyOneVisible visibility; private OnlyOneVisible visibility;
public WorkerCRUDController() { private IMessagesForUser messages;
private Component messagesContainer;
public WorkerCRUDController() {
} }
public WorkerCRUDController(Window createWindow, Window listWindow, public WorkerCRUDController(Window createWindow, Window listWindow,
Window editWindow, IWorkerModel workerModel) { Window editWindow, IWorkerModel workerModel,
IMessagesForUser messages) {
this.createWindow = createWindow; this.createWindow = createWindow;
this.listWindow = listWindow; this.listWindow = listWindow;
this.editWindow = editWindow; this.editWindow = editWindow;
this.workerModel = workerModel; this.workerModel = workerModel;
this.messages = messages;
} }
public Worker getWorker() { public Worker getWorker() {
@ -56,9 +69,17 @@ public class WorkerCRUDController extends GenericForwardComposer {
} }
public void save() { public void save() {
InvalidValue[] invalidValues = workerValidator.getInvalidValues(worker);
if (invalidValues.length > 0) {
for (InvalidValue invalidValue : invalidValues) {
messages.invalidValue(invalidValue);
}
return;
}
workerModel.save(worker); workerModel.save(worker);
getVisibility().showOnly(listWindow); getVisibility().showOnly(listWindow);
Util.reloadBindings(listWindow); Util.reloadBindings(listWindow);
messages.showMessage(Level.INFO, "traballador gardado");
worker = null; worker = null;
} }
@ -86,6 +107,9 @@ public class WorkerCRUDController extends GenericForwardComposer {
super.doAfterCompose(comp); super.doAfterCompose(comp);
comp.setVariable("controller", this, true); comp.setVariable("controller", this, true);
getVisibility().showOnly(listWindow); getVisibility().showOnly(listWindow);
if (messagesContainer == null)
throw new RuntimeException("messagesContainer is needed");
messages = new MessagesForUser(messagesContainer);
} }
private OnlyOneVisible getVisibility() { private OnlyOneVisible getVisibility() {

View file

@ -11,6 +11,8 @@
<window self="@{define(content)}" <window self="@{define(content)}"
apply="org.navalplanner.web.resources.WorkerCRUDController" apply="org.navalplanner.web.resources.WorkerCRUDController"
sclass="workerwindow"> sclass="workerwindow">
<vbox id="messagesContainer">
</vbox>
<list top_id="listWindow" /> <list top_id="listWindow" />
<edition top_id="createWindow" title="Create" <edition top_id="createWindow" title="Create"
save_button_label="Save" cancel_button_label="Cancel" /> save_button_label="Save" cancel_button_label="Cancel" />

View file

@ -1,9 +1,12 @@
package org.navalplanner.web.resources; package org.navalplanner.web.resources;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.createNiceMock; import static org.easymock.EasyMock.createNiceMock;
import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.expectLastCall;
import static org.easymock.EasyMock.isA;
import static org.easymock.EasyMock.replay; import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify; import static org.easymock.EasyMock.verify;
@ -11,8 +14,12 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import org.hibernate.validator.ClassValidator;
import org.hibernate.validator.InvalidValue;
import org.junit.Test; import org.junit.Test;
import org.navalplanner.business.resources.entities.Worker; import org.navalplanner.business.resources.entities.Worker;
import org.navalplanner.web.common.IMessagesForUser;
import org.navalplanner.web.common.Level;
import org.zkoss.zul.api.Window; import org.zkoss.zul.api.Window;
/** /**
@ -27,31 +34,44 @@ public class WorkerCRUDControllerTest {
private WorkerCRUDController createControllerForModel( private WorkerCRUDController createControllerForModel(
IWorkerModel workerModel) { IWorkerModel workerModel) {
return createControllerForModel(workerModel, null);
}
private WorkerCRUDController createControllerForModel(
IWorkerModel workerModel, IMessagesForUser messages) {
createWindow = createNiceMock(Window.class); createWindow = createNiceMock(Window.class);
listWindow = createNiceMock(Window.class); listWindow = createNiceMock(Window.class);
editWindow = createNiceMock(Window.class); editWindow = createNiceMock(Window.class);
WorkerCRUDController workerCRUDController = new WorkerCRUDController( WorkerCRUDController workerCRUDController = new WorkerCRUDController(
createWindow, listWindow, editWindow, workerModel); createWindow, listWindow, editWindow, workerModel, messages);
return workerCRUDController; return workerCRUDController;
} }
@Test @Test
public void testSave() throws Exception { public void testSave() throws Exception {
IWorkerModel workerModel = createMock(IWorkerModel.class); IWorkerModel workerModel = createMock(IWorkerModel.class);
IMessagesForUser messagesForUser = createMock(IMessagesForUser.class);
Worker workerToReturn = new Worker(); Worker workerToReturn = new Worker();
WorkerCRUDController workerCRUDController = createControllerForModel(workerModel); WorkerCRUDController workerCRUDController = createControllerForModel(
workerModel, messagesForUser);
replay(createWindow, listWindow, editWindow); replay(createWindow, listWindow, editWindow);
// expectations // expectations
expect(workerModel.createNewInstance()).andReturn(workerToReturn); expect(workerModel.createNewInstance()).andReturn(workerToReturn);
workerModel.save(workerToReturn); workerModel.save(workerToReturn);
replay(workerModel); messagesForUser.showMessage(Level.INFO,
isA(String.class));
replay(workerModel, messagesForUser);
// action // action
workerCRUDController.goToCreateForm(); workerCRUDController.goToCreateForm();
workerToReturn.setFirstName("first");
workerToReturn.setSurname("blabla");
workerToReturn.setNif("11111");
workerToReturn.setDailyHours(2);
workerCRUDController.save(); workerCRUDController.save();
// verify // verify
verify(workerModel); verify(workerModel, messagesForUser);
} }
@Test @Test
@ -75,7 +95,9 @@ public class WorkerCRUDControllerTest {
@Test @Test
public void testEditWorker() throws Exception { public void testEditWorker() throws Exception {
IWorkerModel workerModel = createMock(IWorkerModel.class); IWorkerModel workerModel = createMock(IWorkerModel.class);
WorkerCRUDController workerCRUDController = createControllerForModel(workerModel); IMessagesForUser messagesForUser = createMock(IMessagesForUser.class);
WorkerCRUDController workerCRUDController = createControllerForModel(
workerModel, messagesForUser);
List<Worker> workersToReturn = new ArrayList<Worker>(Arrays.asList( List<Worker> workersToReturn = new ArrayList<Worker>(Arrays.asList(
new Worker("firstName", "surname", "nif", 4), new Worker( new Worker("firstName", "surname", "nif", 4), new Worker(
"firstName", "surname", "nif", 4))); "firstName", "surname", "nif", 4)));
@ -83,13 +105,40 @@ public class WorkerCRUDControllerTest {
expect(workerModel.getWorkers()).andReturn(workersToReturn); expect(workerModel.getWorkers()).andReturn(workersToReturn);
expect(editWindow.setVisible(true)).andReturn(false); expect(editWindow.setVisible(true)).andReturn(false);
workerModel.save(workersToReturn.get(0)); workerModel.save(workersToReturn.get(0));
replay(createWindow, listWindow, editWindow, workerModel); messagesForUser.showMessage(Level.INFO,
isA(String.class));
replay(createWindow, listWindow, editWindow, workerModel,
messagesForUser);
// perform actions // perform actions
List<Worker> workers = workerCRUDController.getWorkers(); List<Worker> workers = workerCRUDController.getWorkers();
assertEquals(workersToReturn, workers); assertEquals(workersToReturn, workers);
workerCRUDController.goToEditForm(workers.get(0)); workerCRUDController.goToEditForm(workers.get(0));
workerCRUDController.save(); workerCRUDController.save();
// verify // verify
verify(workerModel, editWindow); verify(workerModel, editWindow, messagesForUser);
}
@Test
public void testWorkerInvalid() {
IWorkerModel workerModel = createMock(IWorkerModel.class);
IMessagesForUser messages = createMock(IMessagesForUser.class);
WorkerCRUDController workerCRUDController = createControllerForModel(
workerModel, messages);
Worker workerToReturn = new Worker();
// expectations
expect(workerModel.createNewInstance()).andReturn(workerToReturn);
ClassValidator<Worker> workerValidator = new ClassValidator<Worker>(
Worker.class);
InvalidValue[] invalidValues = workerValidator
.getInvalidValues(workerToReturn);
assertFalse(invalidValues.length == 0);
messages.invalidValue(isA(InvalidValue.class));
expectLastCall().times(invalidValues.length);
replay(createWindow, listWindow, editWindow, workerModel, messages);
// perform actions
workerCRUDController.goToCreateForm();
workerCRUDController.save();
// verify
verify(messages);
} }
} }

766
pom.xml
View file

@ -1,410 +1,418 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.navalplanner</groupId> <groupId>org.navalplanner</groupId>
<artifactId>navalplanner</artifactId> <artifactId>navalplanner</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
<version>1.0.0</version> <version>1.0.0</version>
<name>Naval Planner</name> <name>Naval Planner</name>
<!-- <!--
=================================================================== ===================================================================
--> -->
<!-- Modules --> <!-- Modules -->
<modules> <modules>
<module>navalplanner-business</module> <module>navalplanner-business</module>
<module>navalplanner-gantt-zk</module> <module>navalplanner-gantt-zk</module>
<module>navalplanner-webapp</module> <module>navalplanner-webapp</module>
</modules> </modules>
<!-- <!--
=================================================================== ===================================================================
--> -->
<!-- <!--
Default values for properties. These default values are expected to be Default values for properties. These default values are expected
valid for most profiles. Specific profiles can overwrite values when to be valid for most profiles. Specific profiles can overwrite
necessary. values when necessary.
--> -->
<properties> <properties>
<!-- Data source properties --> <!-- Data source properties -->
<dataSource.user>naval</dataSource.user> <dataSource.user>naval</dataSource.user>
<dataSource.password>naval</dataSource.password> <dataSource.password>naval</dataSource.password>
<dataSource.jndiName>jdbc/navalplanner-ds</dataSource.jndiName> <dataSource.jndiName>jdbc/navalplanner-ds</dataSource.jndiName>
<testDataSource.user>${dataSource.user}</testDataSource.user> <testDataSource.user>${dataSource.user}</testDataSource.user>
<testDataSource.password>${dataSource.password}</testDataSource.password> <testDataSource.password>${dataSource.password}</testDataSource.password>
</properties> </properties>
<!-- <!--
=================================================================== ===================================================================
--> -->
<!-- <!--
Profiles. * The build is always executed by selecting at least two Profiles. * The build is always executed by selecting at least
non-exclusive profiles. By default, such profiles are "dev" and two non-exclusive profiles. By default, such profiles are "dev"
"postgresql" (meaning "use PostgreSQL assuming a development and "postgresql" (meaning "use PostgreSQL assuming a development
environment"). * General profiles. There are two general environment"). * General profiles. There are two general
(database-independent) profiles: "dev" and "prod". The former is used (database-independent) profiles: "dev" and "prod". The former is
for development (including testing) and the latter is used for used for development (including testing) and the latter is used
production (including testing). As shown below, two dataSources for production (including testing). As shown below, two
(databases schemas) are used in both profiles: one for running dataSources (databases schemas) are used in both profiles: one
(dataSource) and another one for the Maven test fase (testDataSource). for running (dataSource) and another one for the Maven test fase
Note the Maven test fase is executed both with development and (testDataSource). Note the Maven test fase is executed both with
production profiles. * Database-specific profiles. There is a profile development and production profiles. * Database-specific
for each supported database. * Specific profiles can be defined to profiles. There is a profile for each supported database. *
better adapt to a particular environment by overwriting/adding Specific profiles can be defined to better adapt to a particular
properties and/or including other chunks of valid XML. * Usage: + mvn environment by overwriting/adding properties and/or including
<<goal>> => Execute <<goal>> with default profiles. + mvn other chunks of valid XML. * Usage: + mvn <<goal>> => Execute
-Pdev,<<database>> <<goal> => Execute <<goal>> with "dev" and <<goal>> with default profiles. + mvn -Pdev,<<database>> <<goal>
<<database>> profiles. + mvn -Pprod,<<database>> <<goal>> => Execute => Execute <<goal>> with "dev" and <<database>> profiles. + mvn
<<goal>> with "prod" and <<database>> profiles. + Note that when using -Pprod,<<database>> <<goal>> => Execute <<goal>> with "prod" and
-P option all desired profiles must be specified (e.g. "-Pprod" with <<database>> profiles. + Note that when using -P option all
the intention to select "prod" and the default database profile is not desired profiles must be specified (e.g. "-Pprod" with the
correct; "-Pprod,<<database>>" must be used instead). * Examples: + intention to select "prod" and the default database profile is
mvn <<goal>> + mvn -Ppostgresql,prod <<goal>> + mvn -Ppostgresql,dev not correct; "-Pprod,<<database>>" must be used instead). *
<<goal>> Examples: + mvn <<goal>> + mvn -Ppostgresql,prod <<goal>> + mvn
--> -Ppostgresql,dev <<goal>>
<profiles> -->
<profiles>
<!-- Development profile --> <!-- Development profile -->
<profile> <profile>
<id>dev</id> <id>dev</id>
<activation> <activation>
<activeByDefault>true</activeByDefault> <activeByDefault>true</activeByDefault>
</activation> </activation>
<properties> <properties>
<!-- Naval Planner environment properties --> <!-- Naval Planner environment properties -->
<navalplanner.mode>dev</navalplanner.mode> <navalplanner.mode>dev</navalplanner.mode>
<!-- Hibernate properties --> <!-- Hibernate properties -->
<hibernate.show_sql>true</hibernate.show_sql> <hibernate.show_sql>true</hibernate.show_sql>
<hibernate.format_sql>true</hibernate.format_sql> <hibernate.format_sql>true</hibernate.format_sql>
<hibernate.use_sql_comments>true</hibernate.use_sql_comments> <hibernate.use_sql_comments>true</hibernate.use_sql_comments>
<hibernate.hbm2ddl.auto>update</hibernate.hbm2ddl.auto> <hibernate.hbm2ddl.auto>update</hibernate.hbm2ddl.auto>
</properties> </properties>
</profile> </profile>
<!-- Production profile --> <!-- Production profile -->
<profile> <profile>
<id>prod</id> <id>prod</id>
<properties> <properties>
<!-- Naval Planner environment properties --> <!-- Naval Planner environment properties -->
<navalplanner.mode>prod</navalplanner.mode> <navalplanner.mode>prod</navalplanner.mode>
<!-- Hibernate properties --> <!-- Hibernate properties -->
<hibernate.show_sql>false</hibernate.show_sql> <hibernate.show_sql>false</hibernate.show_sql>
<hibernate.format_sql>false</hibernate.format_sql> <hibernate.format_sql>false</hibernate.format_sql>
<hibernate.use_sql_comments>false</hibernate.use_sql_comments> <hibernate.use_sql_comments>false</hibernate.use_sql_comments>
<hibernate.hbm2ddl.auto>update</hibernate.hbm2ddl.auto> <hibernate.hbm2ddl.auto>update</hibernate.hbm2ddl.auto>
</properties> </properties>
</profile> </profile>
<!-- PostgreSQL profile --> <!-- PostgreSQL profile -->
<profile> <profile>
<id>postgresql</id> <id>postgresql</id>
<activation> <activation>
<activeByDefault>true</activeByDefault> <activeByDefault>true</activeByDefault>
</activation> </activation>
<properties> <properties>
<!-- JDBC driver properties --> <!-- JDBC driver properties -->
<jdbcDriver.groupId>postgresql</jdbcDriver.groupId> <jdbcDriver.groupId>postgresql</jdbcDriver.groupId>
<jdbcDriver.artifactId>postgresql</jdbcDriver.artifactId> <jdbcDriver.artifactId>postgresql</jdbcDriver.artifactId>
<jdbcDriver.version>8.3-603.jdbc4</jdbcDriver.version> <jdbcDriver.version>8.3-603.jdbc4</jdbcDriver.version>
<jdbcDriver.className>org.postgresql.Driver</jdbcDriver.className> <jdbcDriver.className>org.postgresql.Driver</jdbcDriver.className>
<!-- Data source properties --> <!-- Data source properties -->
<dataSource.url>jdbc:postgresql://localhost/naval${navalplanner.mode}</dataSource.url> <dataSource.url>jdbc:postgresql://localhost/naval${navalplanner.mode}</dataSource.url>
<testDataSource.url>${dataSource.url}test</testDataSource.url> <testDataSource.url>${dataSource.url}test</testDataSource.url>
<!-- Hibernate properties --> <!-- Hibernate properties -->
<hibernate.dialect>org.hibernate.dialect.PostgreSQLDialect</hibernate.dialect> <hibernate.dialect>org.hibernate.dialect.PostgreSQLDialect</hibernate.dialect>
</properties> </properties>
</profile> </profile>
<!-- MySQL profile --> <!-- MySQL profile -->
<profile> <profile>
<id>mysql</id> <id>mysql</id>
<properties> <properties>
<!-- JDBC driver properties --> <!-- JDBC driver properties -->
<jdbcDriver.groupId>mysql</jdbcDriver.groupId> <jdbcDriver.groupId>mysql</jdbcDriver.groupId>
<jdbcDriver.artifactId>mysql-connector-java</jdbcDriver.artifactId> <jdbcDriver.artifactId>mysql-connector-java</jdbcDriver.artifactId>
<jdbcDriver.version>5.0.5</jdbcDriver.version> <jdbcDriver.version>5.0.5</jdbcDriver.version>
<jdbcDriver.className>com.mysql.jdbc.Driver</jdbcDriver.className> <jdbcDriver.className>com.mysql.jdbc.Driver</jdbcDriver.className>
<!-- Data source properties --> <!-- Data source properties -->
<dataSource.url>jdbc:mysql://localhost/naval${navalplanner.mode}</dataSource.url> <dataSource.url>jdbc:mysql://localhost/naval${navalplanner.mode}</dataSource.url>
<testDataSource.url>${dataSource.url}test</testDataSource.url> <testDataSource.url>${dataSource.url}test</testDataSource.url>
<!-- Hibernate properties --> <!-- Hibernate properties -->
<hibernate.dialect>org.hibernate.dialect.MySQLDialect</hibernate.dialect> <hibernate.dialect>org.hibernate.dialect.MySQLDialect</hibernate.dialect>
</properties> </properties>
</profile> </profile>
<!-- HSQLDB profile --> <!-- HSQLDB profile -->
<profile> <profile>
<id>hsqldb</id> <id>hsqldb</id>
<properties> <properties>
<!-- JDBC driver properties --> <!-- JDBC driver properties -->
<jdbcDriver.groupId>hsqldb</jdbcDriver.groupId> <jdbcDriver.groupId>hsqldb</jdbcDriver.groupId>
<jdbcDriver.artifactId>hsqldb</jdbcDriver.artifactId> <jdbcDriver.artifactId>hsqldb</jdbcDriver.artifactId>
<jdbcDriver.version>1.8.0.7</jdbcDriver.version> <jdbcDriver.version>1.8.0.7</jdbcDriver.version>
<jdbcDriver.className>org.hsqldb.jdbcDriver</jdbcDriver.className> <jdbcDriver.className>org.hsqldb.jdbcDriver</jdbcDriver.className>
<!-- Data source properties --> <!-- Data source properties -->
<dataSource.user>sa</dataSource.user> <dataSource.user>sa</dataSource.user>
<dataSource.password /> <dataSource.password />
<dataSource.url>jdbc:hsqldb:${java.io.tmpdir}/naval${navalplanner.mode};shutdown=true</dataSource.url> <dataSource.url>jdbc:hsqldb:${java.io.tmpdir}/naval${navalplanner.mode};shutdown=true</dataSource.url>
<testDataSource.url>jdbc:hsqldb:${java.io.tmpdir}/naval${navalplanner.mode}test;shutdown=true</testDataSource.url> <testDataSource.url>jdbc:hsqldb:${java.io.tmpdir}/naval${navalplanner.mode}test;shutdown=true</testDataSource.url>
<!-- Hibernate properties --> <!-- Hibernate properties -->
<hibernate.dialect>org.hibernate.dialect.HSQLDialect</hibernate.dialect> <hibernate.dialect>org.hibernate.dialect.HSQLDialect</hibernate.dialect>
</properties> </properties>
</profile> </profile>
</profiles> </profiles>
<!-- <!--
=================================================================== ===================================================================
--> -->
<!-- Dependency management --> <!-- Dependency management -->
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<!-- JDBC driver --> <!-- JDBC driver -->
<dependency> <dependency>
<groupId>${jdbcDriver.groupId}</groupId> <groupId>${jdbcDriver.groupId}</groupId>
<artifactId>${jdbcDriver.artifactId}</artifactId> <artifactId>${jdbcDriver.artifactId}</artifactId>
<version>${jdbcDriver.version}</version> <version>${jdbcDriver.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- Hibernate --> <!-- Hibernate -->
<dependency> <dependency>
<groupId>org.hibernate</groupId> <groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId> <artifactId>hibernate</artifactId>
<version>3.2.6.ga</version> <version>3.2.6.ga</version>
</dependency> </dependency>
<!-- JUnit --> <dependency>
<!-- <groupId>org.hibernate</groupId>
IMPORTANT: Spring TestContext 2.5.x is not compatible with JUnit <artifactId>hibernate-annotations</artifactId>
4.5. <version>3.3.1.GA</version>
--> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>org.hibernate</groupId>
<artifactId>junit</artifactId> <artifactId>hibernate-validator</artifactId>
<version>4.4</version> <version>3.0.0.ga</version>
<scope>test</scope> </dependency>
</dependency> <!-- JUnit -->
<!--
IMPORTANT: Spring TestContext 2.5.x is not compatible
with JUnit 4.5.
-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>
<!-- Easy mock --> <!-- Easy mock -->
<dependency> <dependency>
<groupId>org.easymock</groupId> <groupId>org.easymock</groupId>
<artifactId>easymock</artifactId> <artifactId>easymock</artifactId>
<version>2.4</version> <version>2.4</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- Spring --> <!-- Spring -->
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring</artifactId> <artifactId>spring</artifactId>
<version>2.5.6</version> <version>2.5.6</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.springframework</groupId> <groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId> <artifactId>spring-test</artifactId>
<version>2.5.6</version> <version>2.5.6</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- Commons Logging (required by many frameworks)--> <!-- Commons Logging (required by many frameworks)-->
<dependency> <dependency>
<groupId>commons-logging</groupId> <groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId> <artifactId>commons-logging</artifactId>
<version>1.0.4</version> <version>1.0.4</version>
</dependency> </dependency>
<!-- BeanShell (required by ZK)--> <!-- BeanShell (required by ZK)-->
<dependency> <dependency>
<groupId>org.beanshell</groupId> <groupId>org.beanshell</groupId>
<artifactId>bsh</artifactId> <artifactId>bsh</artifactId>
<version>2.0b4</version> <version>2.0b4</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<!-- Apache Commons Fileupload (required by ZK) --> <!-- Apache Commons Fileupload (required by ZK) -->
<dependency> <dependency>
<groupId>commons-fileupload</groupId> <groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId> <artifactId>commons-fileupload</artifactId>
<version>1.2.1</version> <version>1.2.1</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<!-- ZK --> <!-- ZK -->
<dependency> <dependency>
<groupId>org.zkoss.zk</groupId> <groupId>org.zkoss.zk</groupId>
<artifactId>zul</artifactId> <artifactId>zul</artifactId>
<version>3.6.1</version> <version>3.6.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.zkoss.zk</groupId> <groupId>org.zkoss.zk</groupId>
<artifactId>zkplus</artifactId> <artifactId>zkplus</artifactId>
<version>3.6.1</version> <version>3.6.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.zkoss.zk</groupId> <groupId>org.zkoss.zk</groupId>
<artifactId>zk</artifactId> <artifactId>zk</artifactId>
<version>3.6.1</version> <version>3.6.1</version>
</dependency> </dependency>
<!-- JGraphT --> <!-- JGraphT -->
<dependency> <dependency>
<groupId>org.jgrapht</groupId> <groupId>org.jgrapht</groupId>
<artifactId>jgrapht-jdk1.5</artifactId> <artifactId>jgrapht-jdk1.5</artifactId>
<version>0.7.3</version> <version>0.7.3</version>
</dependency> </dependency>
<!-- Naval Planner ZK Components --> <!-- Naval Planner ZK Components -->
<dependency> <dependency>
<groupId>org.navalplanner</groupId> <groupId>org.navalplanner</groupId>
<artifactId>navalplanner-gantt-zk</artifactId> <artifactId>navalplanner-gantt-zk</artifactId>
<version>1.0.0</version> <version>1.0.0</version>
</dependency> </dependency>
<!-- Naval Planner Business --> <!-- Naval Planner Business -->
<dependency> <dependency>
<groupId>org.navalplanner</groupId> <groupId>org.navalplanner</groupId>
<artifactId>navalplanner-business</artifactId> <artifactId>navalplanner-business</artifactId>
<version>1.0.0</version> <version>1.0.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>javax.servlet</groupId> <groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId> <artifactId>servlet-api</artifactId>
<version>2.5</version> <version>2.5</version>
<type>jar</type> <type>jar</type>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>
<build> <build>
<!-- =============================================================== --> <!-- =============================================================== -->
<!-- Filtering --> <!-- Filtering -->
<resources> <resources>
<!-- <!--
Apply filtering to files matching the following expressions in Apply filtering to files matching the following
src/main/resources. expressions in src/main/resources.
--> -->
<resource> <resource>
<directory>src/main/resources</directory> <directory>src/main/resources</directory>
<filtering>true</filtering> <filtering>true</filtering>
<includes> <includes>
<include>*spring-config.xml</include> <include>*spring-config.xml</include>
<include>*hibernate.cfg.xml</include> <include>*hibernate.cfg.xml</include>
</includes> </includes>
</resource> </resource>
<!-- <!--
Continue considering resources the files in src/main/resources, but Continue considering resources the files in
without applying filtering. src/main/resources, but without applying filtering.
--> -->
<resource> <resource>
<directory>src/main/resources</directory> <directory>src/main/resources</directory>
</resource> </resource>
<!-- Filter Jetty configuration --> <!-- Filter Jetty configuration -->
<resource> <resource>
<directory>../src/main/jetty</directory> <directory>../src/main/jetty</directory>
<includes> <includes>
<include>jetty-env.xml</include> <include>jetty-env.xml</include>
</includes> </includes>
<targetPath>../jetty</targetPath> <targetPath>../jetty</targetPath>
<filtering>true</filtering> <filtering>true</filtering>
</resource> </resource>
</resources> </resources>
<testResources> <testResources>
<!-- <!--
Apply filtering to files matching the following expressions in Apply filtering to files matching the following
src/test/resources. expressions in src/test/resources.
--> -->
<testResource> <testResource>
<directory>src/test/resources</directory> <directory>src/test/resources</directory>
<filtering>true</filtering> <filtering>true</filtering>
<includes> <includes>
<include>*spring-config-test.xml</include> <include>*spring-config-test.xml</include>
<include>*hibernate-test.cfg.xml</include> <include>*hibernate-test.cfg.xml</include>
</includes> </includes>
</testResource> </testResource>
<!-- <!--
Continue considering resources the files in src/test/resources, but Continue considering resources the files in
without applying filtering. src/test/resources, but without applying filtering.
--> -->
<testResource> <testResource>
<directory>src/test/resources</directory> <directory>src/test/resources</directory>
</testResource> </testResource>
</testResources> </testResources>
<plugins> <plugins>
<!-- =========================================================== --> <!-- =========================================================== -->
<!-- Compiler configuration --> <!-- Compiler configuration -->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<configuration> <configuration>
<verbose>true</verbose> <verbose>true</verbose>
<source>1.6</source> <source>1.6</source>
<target>1.6</target> <target>1.6</target>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
</configuration> </configuration>
</plugin> </plugin>
<!-- =========================================================== --> <!-- =========================================================== -->
<!-- Assembly configuration --> <!-- Assembly configuration -->
<plugin> <plugin>
<artifactId>maven-assembly-plugin</artifactId> <artifactId>maven-assembly-plugin</artifactId>
<configuration> <configuration>
<descriptors> <descriptors>
<descriptor>src/main/assembly/src.xml</descriptor> <descriptor>src/main/assembly/src.xml</descriptor>
</descriptors> </descriptors>
</configuration> </configuration>
</plugin> </plugin>
<!-- =========================================================== --> <!-- =========================================================== -->
<!-- Jetty configuration --> <!-- Jetty configuration -->
<plugin> <plugin>
<groupId>org.mortbay.jetty</groupId> <groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId> <artifactId>maven-jetty-plugin</artifactId>
<!-- FIXME: try to not to specify version. --> <!-- FIXME: try to not to specify version. -->
<version>6.1.12.rc2</version> <version>6.1.12.rc2</version>
<configuration> <configuration>
<jettyEnvXml>target/jetty/jetty-env.xml</jettyEnvXml> <jettyEnvXml>target/jetty/jetty-env.xml</jettyEnvXml>
<scanIntervalSeconds>5</scanIntervalSeconds> <scanIntervalSeconds>5</scanIntervalSeconds>
<scanTargetPatterns> <scanTargetPatterns>
<scanTargetPattern> <scanTargetPattern>
<directory>src/main/webapp/WEB-INF</directory> <directory>src/main/webapp/WEB-INF</directory>
<includes> <includes>
<include>*</include> <include>*</include>
</includes> </includes>
</scanTargetPattern> </scanTargetPattern>
</scanTargetPatterns> </scanTargetPatterns>
<!-- Log to the console. --> <!-- Log to the console. -->
<requestLog implementation="org.mortbay.jetty.NCSARequestLog"> <requestLog implementation="org.mortbay.jetty.NCSARequestLog">
<!-- <!--
This do anything for Jetty, but is a workaround for a Maven bug This do anything for Jetty, but is a
that prevents the requestLog from being set. workaround for a Maven bug that prevents the
--> requestLog from being set.
<append>true</append> -->
</requestLog> <append>true</append>
<!-- </requestLog>
<connectors> <connector <!--
implementation="org.mortbay.jetty.nio.SelectChannelConnector"> <connectors> <connector
<port>9090</port> </connector> </connectors> implementation="org.mortbay.jetty.nio.SelectChannelConnector">
--> <port>9090</port> </connector> </connectors>
</configuration> -->
</configuration>
<dependencies>
<dependency>
<groupId>${jdbcDriver.groupId}</groupId>
<artifactId>${jdbcDriver.artifactId}</artifactId>
<version>${jdbcDriver.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>${jdbcDriver.groupId}</groupId>
<artifactId>${jdbcDriver.artifactId}</artifactId>
<version>${jdbcDriver.version}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project> </project>