diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/Registry.java b/libreplan-business/src/main/java/org/libreplan/business/common/Registry.java
index 82cbf840f..441e6d264 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/common/Registry.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/Registry.java
@@ -28,7 +28,9 @@ import org.libreplan.business.calendars.daos.ICalendarDataDAO;
import org.libreplan.business.calendars.daos.ICalendarExceptionDAO;
import org.libreplan.business.calendars.daos.ICalendarExceptionTypeDAO;
import org.libreplan.business.common.daos.IConfigurationDAO;
+import org.libreplan.business.common.daos.IConnectorDAO;
import org.libreplan.business.common.daos.IEntitySequenceDAO;
+import org.libreplan.business.common.daos.IJobSchedulerConfigurationDAO;
import org.libreplan.business.costcategories.daos.ICostCategoryDAO;
import org.libreplan.business.costcategories.daos.IHourCostDAO;
import org.libreplan.business.costcategories.daos.IResourcesCostCategoryAssignmentDAO;
@@ -44,6 +46,7 @@ import org.libreplan.business.materials.daos.IUnitTypeDAO;
import org.libreplan.business.orders.daos.IHoursGroupDAO;
import org.libreplan.business.orders.daos.IOrderDAO;
import org.libreplan.business.orders.daos.IOrderElementDAO;
+import org.libreplan.business.orders.daos.IOrderSyncInfoDAO;
import org.libreplan.business.planner.daos.ITaskElementDAO;
import org.libreplan.business.qualityforms.daos.IQualityFormDAO;
import org.libreplan.business.resources.daos.ICriterionDAO;
@@ -203,6 +206,15 @@ public class Registry {
@Autowired
private IOrderAuthorizationDAO orderAuthorizationDAO;
+ @Autowired
+ private IConnectorDAO connectorDAO;
+
+ @Autowired
+ private IOrderSyncInfoDAO orderSyncInfoDAO;
+
+ @Autowired
+ private IJobSchedulerConfigurationDAO jobSchedulerConfigurationDAO;
+
@Autowired
private IAdHocTransactionService transactionServiceDAO;
@@ -379,4 +391,15 @@ public class Registry {
return getInstance().orderAuthorizationDAO;
}
+ public static IConnectorDAO getConnectorDAO() {
+ return getInstance().connectorDAO;
+ }
+
+ public static IOrderSyncInfoDAO getOrderSyncInfoDAO() {
+ return getInstance().orderSyncInfoDAO;
+ }
+
+ public static IJobSchedulerConfigurationDAO getJobSchedulerConfigurationDAO() {
+ return getInstance().jobSchedulerConfigurationDAO;
+ }
}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/daos/ConnectorDAO.java b/libreplan-business/src/main/java/org/libreplan/business/common/daos/ConnectorDAO.java
new file mode 100644
index 000000000..884b13879
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/daos/ConnectorDAO.java
@@ -0,0 +1,75 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.daos;
+
+import java.util.List;
+
+import org.hibernate.Criteria;
+import org.hibernate.criterion.Restrictions;
+import org.libreplan.business.common.entities.Connector;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * DAO for {@link Connector} entity.
+ *
+ * @author Miciele Ghiorghis
+ * @author Manuel Rego Casasnovas
+ */
+@Repository
+@Scope(BeanDefinition.SCOPE_SINGLETON)
+public class ConnectorDAO extends GenericDAOHibernate
+ implements IConnectorDAO {
+
+ @Override
+ @Transactional(readOnly = true)
+ public List getAll() {
+ return list(Connector.class);
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Connector findUniqueByName(String name) {
+ Criteria c = getSession().createCriteria(Connector.class).add(
+ Restrictions.eq("name", name));
+ return (Connector) c.uniqueResult();
+ }
+
+ @Override
+ @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+ public boolean existsByNameAnotherTransaction(Connector connector) {
+ return existsOtherConnectorByName(connector);
+ }
+
+ private boolean existsOtherConnectorByName(Connector connector) {
+ Connector found = findUniqueByName(connector.getName());
+ return found != null && found != connector;
+ }
+
+ @Override
+ @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+ public Connector findUniqueByNameAnotherTransaction(String name) {
+ return findUniqueByName(name);
+ }
+
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/daos/IConnectorDAO.java b/libreplan-business/src/main/java/org/libreplan/business/common/daos/IConnectorDAO.java
new file mode 100644
index 000000000..82ab2119c
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/daos/IConnectorDAO.java
@@ -0,0 +1,42 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.daos;
+
+import java.util.List;
+
+import org.libreplan.business.common.entities.Connector;
+
+/**
+ * Contract for {@link Conn}
+ *
+ * @author Miciele Ghiorghis
+ * @author Manuel Rego Casasnovas
+ */
+public interface IConnectorDAO extends IGenericDAO {
+
+ List getAll();
+
+ Connector findUniqueByName(String name);
+
+ boolean existsByNameAnotherTransaction(Connector connector);
+
+ Connector findUniqueByNameAnotherTransaction(String name);
+
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/daos/IJobSchedulerConfigurationDAO.java b/libreplan-business/src/main/java/org/libreplan/business/common/daos/IJobSchedulerConfigurationDAO.java
new file mode 100644
index 000000000..4f1bfe23a
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/daos/IJobSchedulerConfigurationDAO.java
@@ -0,0 +1,80 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.daos;
+
+import java.util.List;
+
+import org.libreplan.business.common.entities.JobSchedulerConfiguration;
+
+/**
+ * Contract for {@link JobSchedulerConfigurationDAO}
+ *
+ * @author Miciele Ghiorghis
+ */
+public interface IJobSchedulerConfigurationDAO extends
+ IGenericDAO {
+
+ /**
+ * Returns all {@link JobSchedulerConfiguration}
+ */
+ List getAll();
+
+ /**
+ * Searches and returns {@link JobSchedulerConfiguration} for the given
+ * connectorName
+ *
+ * @param connectorName
+ * the name of the connector
+ */
+ List findByConnectorName(String connectorName);
+
+ /**
+ * Searches and returns {@link JobSchedulerConfiguration} for the given
+ * jobGroup and jobName
+ *
+ * @param jobGroup
+ * @param jobName
+ */
+ JobSchedulerConfiguration findByJobGroupAndJobName(String jobGroup,
+ String jobName);
+
+ /**
+ * Returns true if there exists other @{link JobSchedulerConfiguration} with
+ * the same {@link JobSchedulerConfiguration#getJobGroup()} and
+ * {@link JobSchedulerConfiguration#getJobName()
+ *
+ * @param jobSchedulerConfiguration
+ * the {@link JobSchedulerConfiguration}
+ */
+ boolean existsByJobGroupAndJobNameAnotherTransaction(
+ JobSchedulerConfiguration jobSchedulerConfiguration);
+
+ /**
+ * Returns unique {@link JobSchedulerConfiguration} for the specified
+ * JobGroup and JobName
+ *
+ * @param jobGroup
+ * the jobGroup
+ * @param jobName
+ * the jobName
+ */
+ JobSchedulerConfiguration findUniqueByJobGroupAndJobNameAnotherTransaction(
+ String jobGroup, String jobName);
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/daos/JobSchedulerConfigurationDAO.java b/libreplan-business/src/main/java/org/libreplan/business/common/daos/JobSchedulerConfigurationDAO.java
new file mode 100644
index 000000000..f07ad1fce
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/daos/JobSchedulerConfigurationDAO.java
@@ -0,0 +1,100 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.daos;
+
+import java.util.List;
+
+import org.hibernate.Criteria;
+import org.hibernate.criterion.Restrictions;
+import org.libreplan.business.common.entities.JobSchedulerConfiguration;
+import org.libreplan.business.orders.entities.OrderSyncInfo;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * DAO for {@link JobSchedulerConfiguration}
+ *
+ * @author Miciele Ghiorghis
+ */
+@Repository
+@Scope(BeanDefinition.SCOPE_SINGLETON)
+public class JobSchedulerConfigurationDAO extends
+ GenericDAOHibernate implements
+ IJobSchedulerConfigurationDAO {
+
+ @Override
+ @Transactional(readOnly = true)
+ public List getAll() {
+ return list(JobSchedulerConfiguration.class);
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public JobSchedulerConfiguration findByJobGroupAndJobName(String jobGroup,
+ String jobName) {
+ return (JobSchedulerConfiguration) getSession()
+ .createCriteria(JobSchedulerConfiguration.class)
+ .add(Restrictions.eq("jobGroup", jobGroup))
+ .add(Restrictions.eq("jobName", jobName)).uniqueResult();
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public List findByConnectorName(
+ String connectorName) {
+ Criteria c = getSession().createCriteria(
+ JobSchedulerConfiguration.class).add(
+ Restrictions.eq("connectorName", connectorName));
+ return ((List) c.list());
+ }
+
+ @Override
+ @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+ public boolean existsByJobGroupAndJobNameAnotherTransaction(
+ JobSchedulerConfiguration jobSchedulerConfiguration) {
+ return existsOtherJobByGroupAndName(jobSchedulerConfiguration);
+ }
+
+ /**
+ * Returns true if other {@link JobSchedulerConfiguration} which is the same
+ * as the given {@link OrderSyncInfo} already exists
+ *
+ * @param jobSchedulerConfiguration
+ * the {@link JobSchedulerConfiguration}
+ */
+ private boolean existsOtherJobByGroupAndName(
+ JobSchedulerConfiguration jobSchedulerConfiguration) {
+ JobSchedulerConfiguration found = findByJobGroupAndJobName(
+ jobSchedulerConfiguration.getJobGroup(),
+ jobSchedulerConfiguration.getJobName());
+ return found != null && found != jobSchedulerConfiguration;
+ }
+
+ @Override
+ @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+ public JobSchedulerConfiguration findUniqueByJobGroupAndJobNameAnotherTransaction(
+ String jobGroup, String jobName) {
+ return findByJobGroupAndJobName(jobGroup, jobName);
+ }
+
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/Configuration.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/Configuration.java
index caf2a1b02..2f05f98d4 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/common/entities/Configuration.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/Configuration.java
@@ -104,8 +104,6 @@ public class Configuration extends BaseEntity {
private Boolean generateCodeForExpenseSheets = true;
- private JiraConfiguration jiraConfiguration;
-
/**
* Currency code according to ISO-4217 (3 letters)
*/
@@ -505,12 +503,4 @@ public class Configuration extends BaseEntity {
this.secondsPlanningWarning = secondsPlanningWarning;
}
- public JiraConfiguration getJiraConfiguration() {
- return jiraConfiguration;
- }
-
- public void setJiraConfiguration(JiraConfiguration jiraConfiguration) {
- this.jiraConfiguration = jiraConfiguration;
- }
-
}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConfigurationBootstrap.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConfigurationBootstrap.java
index 7f9f41c0b..633b4b85f 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConfigurationBootstrap.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConfigurationBootstrap.java
@@ -81,13 +81,6 @@ public class ConfigurationBootstrap implements IConfigurationBootstrap {
}
configuration.setLdapConfiguration(ldapConfiguration);
- JiraConfiguration jiraConfiguration = configuration
- .getJiraConfiguration();
- if (jiraConfiguration == null) {
- jiraConfiguration = JiraConfiguration.create();
- }
- configuration.setJiraConfiguration(jiraConfiguration);
-
configurationDAO.save(configuration);
}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConfigurationTypeOfWorkHoursBootstrap.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConfigurationTypeOfWorkHoursBootstrap.java
index 90cfee44b..c3404b920 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConfigurationTypeOfWorkHoursBootstrap.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConfigurationTypeOfWorkHoursBootstrap.java
@@ -75,8 +75,6 @@ public class ConfigurationTypeOfWorkHoursBootstrap implements
}
configuration.setPersonalTimesheetsTypeOfWorkHours(typeOfWorkHours);
- configuration.getJiraConfiguration().setJiraConnectorTypeOfWorkHours(
- typeOfWorkHours);
configurationDAO.save(configuration);
}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/Connector.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/Connector.java
new file mode 100644
index 000000000..e57458358
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/Connector.java
@@ -0,0 +1,148 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.entities;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.hibernate.validator.AssertTrue;
+import org.hibernate.validator.NotEmpty;
+import org.hibernate.validator.Valid;
+import org.libreplan.business.common.BaseEntity;
+import org.libreplan.business.common.Registry;
+import org.libreplan.business.common.daos.IConnectorDAO;
+
+/**
+ * Connector entity, represents a connector in order that LibrePlan interchange
+ * some data with other application.
+ *
+ * A connector is identified by a name and it has a list of pairs
+ * key-value in order to store the configuration parameters of the connector.
+ *
+ * This entity should be used to create new connectors in LibrePlan.
+ *
+ * @author Miciele Ghiorghis
+ * @author Manuel Rego Casasnovas
+ */
+public class Connector extends BaseEntity {
+
+ public static Connector create(String name) {
+ return create(new Connector(name));
+ }
+
+ private String name;
+
+ private List properties = new ArrayList();
+
+ /**
+ * Constructor for Hibernate. Do not use!
+ */
+ protected Connector() {
+ }
+
+ private Connector(String name) {
+ this.name = name;
+ }
+
+ @NotEmpty(message = "name not specified")
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ @Valid
+ public List getProperties() {
+ return Collections.unmodifiableList(properties);
+ }
+
+ public void setProperties(List properties) {
+ this.properties = properties;
+ }
+
+ public void addProperty(ConnectorProperty property) {
+ properties.add(property);
+ }
+
+ public Map getPropertiesAsMap() {
+ Map map = new HashMap();
+ for (ConnectorProperty property : properties) {
+ map.put(property.getKey(), property.getValue());
+ }
+ return map;
+ }
+
+ @AssertTrue(message = "connector name is already being used")
+ public boolean checkConstraintUniqueConnectorName() {
+ if (StringUtils.isBlank(name)) {
+ return true;
+ }
+
+ IConnectorDAO connectorDAO = Registry.getConnectorDAO();
+ if (isNewObject()) {
+ return !connectorDAO.existsByNameAnotherTransaction(this);
+ } else {
+ Connector found = connectorDAO
+ .findUniqueByNameAnotherTransaction(name);
+ return found == null || found.getId().equals(getId());
+ }
+
+ }
+
+ public boolean isActivated() {
+ return getPropertiesAsMap()
+ .get(PredefinedConnectorProperties.ACTIVATED).equalsIgnoreCase(
+ "Y");
+ }
+
+ /**
+ * Check if connector's connections values are valid
+ *
+ * @return true if connection values are valid
+ */
+ public boolean areConnectionValuesValid() {
+ String serverUrl = getPropertiesAsMap().get(
+ PredefinedConnectorProperties.SERVER_URL);
+ try {
+ new URL(serverUrl);
+ } catch (MalformedURLException e) {
+ return false;
+ }
+
+ if (StringUtils.isBlank(getPropertiesAsMap().get(
+ PredefinedConnectorProperties.USERNAME))) {
+ return false;
+ }
+
+ if (StringUtils.isBlank(getPropertiesAsMap().get(
+ PredefinedConnectorProperties.PASSWORD))) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConnectorBootstrap.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConnectorBootstrap.java
new file mode 100644
index 000000000..a47ff19be
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConnectorBootstrap.java
@@ -0,0 +1,57 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 Igalia, S.L.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.entities;
+
+import org.libreplan.business.common.daos.IConnectorDAO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Creates the LibrePlan {@link Connector Connectors} with its configuration
+ * properties and default values.
+ *
+ * @author Manuel Rego Casasnovas
+ */
+@Component
+@Scope("singleton")
+public class ConnectorBootstrap implements IConnectorBootstrap {
+
+ @Autowired
+ private IConnectorDAO connectorDAO;
+
+ @Override
+ @Transactional
+ public void loadRequiredData() {
+ for (PredefinedConnectors predefinedConnector : PredefinedConnectors
+ .values()) {
+ String name = predefinedConnector.getName();
+
+ Connector connector = connectorDAO.findUniqueByName(name);
+ if (connector == null) {
+ connector = Connector.create(name);
+ connector.setProperties(predefinedConnector.getProperties());
+ connectorDAO.save(connector);
+ }
+ }
+ }
+
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConnectorException.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConnectorException.java
new file mode 100644
index 000000000..062666893
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConnectorException.java
@@ -0,0 +1,33 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.entities;
+
+/**
+ * Exception to ecapsulate connector(values) exceptions
+ *
+ * @author Miciele Ghiorghis
+ */
+public class ConnectorException extends Exception {
+
+ public ConnectorException(String message) {
+ super(message);
+ }
+
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConnectorProperty.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConnectorProperty.java
new file mode 100644
index 000000000..7cb6b79d7
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/ConnectorProperty.java
@@ -0,0 +1,64 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 Igalia, S.L.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.entities;
+
+import org.hibernate.validator.NotEmpty;
+
+/**
+ * This class is intended to work as a Hibernate component. It's formed by two
+ * attributes, the key and the value of the property. It represents the
+ * different configuration parameters of a {@link Connector}.
+ *
+ * @author Manuel Rego Casasnovas
+ */
+public class ConnectorProperty {
+
+ public static ConnectorProperty create(String key, String value) {
+ return new ConnectorProperty(key, value);
+ }
+
+ private String key;
+ private String value;
+
+ /**
+ * Default constructor for Hibernate. Do not use!
+ */
+ protected ConnectorProperty() {
+ }
+
+ private ConnectorProperty(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ @NotEmpty(message = "property key not specified")
+ public String getKey() {
+ return key;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/IConnectorBootstrap.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/IConnectorBootstrap.java
new file mode 100644
index 000000000..d70373b32
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/IConnectorBootstrap.java
@@ -0,0 +1,33 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 Igalia, S.L.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.entities;
+
+import org.libreplan.business.IDataBootstrap;
+
+/**
+ * Contract for {@link ConnectorBootstrap}.
+ *
+ * @author Manuel Rego Casasnovas
+ */
+public interface IConnectorBootstrap extends IDataBootstrap {
+
+ void loadRequiredData();
+
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/JiraConfiguration.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/JiraConfiguration.java
deleted file mode 100755
index f92b504ea..000000000
--- a/libreplan-business/src/main/java/org/libreplan/business/common/entities/JiraConfiguration.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * This file is part of LibrePlan
- *
- * Copyright (C) 2013 St. Antoniusziekenhuis
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-package org.libreplan.business.common.entities;
-
-import org.libreplan.business.common.BaseEntity;
-import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
-
-/**
- * JiraConfiguration entity
- *
- * @author Miciele Ghiorghis
- */
-public class JiraConfiguration extends BaseEntity {
-
- /**
- * Code prefix for different entities integrated with JIRA.
- */
- public static final String CODE_PREFIX = "JIRA-";
-
- public static JiraConfiguration create() {
- return create(new JiraConfiguration());
- }
-
- private boolean jiraActivated;
-
- private String jiraUrl;
-
- /**
- * Stores one of the next 2 options:
- *
- * - A comma-separated list of labels
- * - A URL that will return a comma-separated list of labels
- *
- */
- private String jiraLabels;
-
- private String jiraUserId;
-
- private String jiraPassword;
-
- private TypeOfWorkHours jiraConnectorTypeOfWorkHours;
-
- /**
- * Constructor for Hibernate. Do not use!
- */
- protected JiraConfiguration() {
- }
-
- public boolean isJiraActivated() {
- return jiraActivated;
- }
-
- public void setJiraActivated(boolean jiraActivated) {
- this.jiraActivated = jiraActivated;
- }
-
- public String getJiraUrl() {
- return jiraUrl;
- }
-
- public void setJiraUrl(String jiraUrl) {
- this.jiraUrl = jiraUrl;
- }
-
- public String getJiraLabels() {
- return jiraLabels;
- }
-
- public void setJiraLabels(String jiraLabels) {
- this.jiraLabels = jiraLabels;
- }
-
- public String getJiraUserId() {
- return jiraUserId;
- }
-
- public void setJiraUserId(String jiraUserId) {
- this.jiraUserId = jiraUserId;
- }
-
- public String getJiraPassword() {
- return jiraPassword;
- }
-
- public void setJiraPassword(String jiraPassword) {
- this.jiraPassword = jiraPassword;
- }
-
- public TypeOfWorkHours getJiraConnectorTypeOfWorkHours() {
- return jiraConnectorTypeOfWorkHours;
- }
-
- public void setJiraConnectorTypeOfWorkHours(TypeOfWorkHours typeOfWorkHours) {
- jiraConnectorTypeOfWorkHours = typeOfWorkHours;
- }
-
-}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobClassNameEnum.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobClassNameEnum.java
new file mode 100644
index 000000000..038d7c413
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobClassNameEnum.java
@@ -0,0 +1,51 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.entities;
+
+
+/**
+ * Defines the job class package and name to be used as data type in
+ * {@link JobSchedulerConfiguration}
+ *
+ * @author Miciele Ghiorghis
+ */
+public enum JobClassNameEnum {
+
+ IMPORT_ROSTER_FROM_TIM_JOB("org.libreplan.importers", "ImportRosterFromTimJob"),
+ EXPORT_TIMESHEET_TO_TIM_JOB("org.libreplan.importers","ExportTimesheetToTimJob"),
+ SYNC_ORDERELEMENTS_WITH_JIRA_ISSUES_JOB("org.libreplan.importers","JiraOrderElementSynchronizerJob");
+
+ private String packageName;
+ private String name;
+
+ private JobClassNameEnum(String packageName, String name) {
+ this.packageName = packageName;
+ this.name = name;
+ }
+
+ public String getPackageName() {
+ return packageName;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobSchedulerConfiguration.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobSchedulerConfiguration.java
new file mode 100644
index 000000000..d9f970d8a
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/JobSchedulerConfiguration.java
@@ -0,0 +1,140 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.entities;
+
+import org.apache.commons.lang.StringUtils;
+import org.hibernate.validator.AssertTrue;
+import org.hibernate.validator.NotNull;
+import org.libreplan.business.common.BaseEntity;
+import org.libreplan.business.common.IHumanIdentifiable;
+import org.libreplan.business.common.Registry;
+import org.libreplan.business.common.daos.IJobSchedulerConfigurationDAO;
+
+/**
+ * JobSchedulerConfiguration entity, represents parameters for the jobs to be
+ * scheduled. This entity is used by the SchedulerManager to
+ * schedule jobs and in UI to show the scheduler status.
+ *
+ * The jobGroup and jobName together forms a job key
+ * and non of the fields must be null. Moreover it should contain a valid
+ * cronExpression
+ *
+ * @author Miciele Ghiorghis
+ */
+public class JobSchedulerConfiguration extends BaseEntity implements
+ IHumanIdentifiable {
+
+ public static JobSchedulerConfiguration create() {
+ return create(new JobSchedulerConfiguration());
+ }
+
+ /**
+ * Constructor for Hibernate. Do not use!
+ */
+ protected JobSchedulerConfiguration() {
+ }
+
+ private String jobGroup;
+
+ private String jobName;
+
+ private String cronExpression;
+
+ private JobClassNameEnum jobClassName;
+
+ private boolean schedule;
+
+ private String connectorName;
+
+ @NotNull(message = "job group not specified")
+ public String getJobGroup() {
+ return jobGroup;
+ }
+
+ public void setJobGroup(String jobGroup) {
+ this.jobGroup = jobGroup;
+ }
+
+ @NotNull(message = "job name not specified")
+ public String getJobName() {
+ return jobName;
+ }
+
+ public void setJobName(String jobName) {
+ this.jobName = jobName;
+ }
+
+ @NotNull(message = "cron expression not specified")
+ public String getCronExpression() {
+ return cronExpression;
+ }
+
+ public void setCronExpression(String cronExpression) {
+ this.cronExpression = cronExpression;
+ }
+
+ @NotNull(message = "job class name not specified")
+ public JobClassNameEnum getJobClassName() {
+ return jobClassName;
+ }
+
+ public void setJobClassName(JobClassNameEnum jobClassName) {
+ this.jobClassName = jobClassName;
+ }
+
+ public boolean isSchedule() {
+ return schedule;
+ }
+
+ public void setSchedule(boolean schedule) {
+ this.schedule = schedule;
+ }
+
+ public String getConnectorName() {
+ return connectorName;
+ }
+
+ public void setConnectorName(String connectorName) {
+ this.connectorName = connectorName;
+ }
+
+ @Override
+ public String getHumanId() {
+ return jobGroup == null ? "" : jobGroup;
+ }
+
+ @AssertTrue(message = "job group and name are already being used")
+ public boolean checkConstraintUniqueJobGroupAndName() {
+ if (StringUtils.isBlank(jobGroup) && StringUtils.isBlank(jobName)) {
+ return true;
+ }
+ IJobSchedulerConfigurationDAO jobSchedulerConfigurationDAO = Registry
+ .getJobSchedulerConfigurationDAO();
+ if (isNewObject()) {
+ return !jobSchedulerConfigurationDAO
+ .existsByJobGroupAndJobNameAnotherTransaction(this);
+ } else {
+ JobSchedulerConfiguration found = jobSchedulerConfigurationDAO
+ .findUniqueByJobGroupAndJobNameAnotherTransaction(jobGroup,
+ jobName);
+ return found == null || found.getId().equals(getId());
+ }
+ }
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/PredefinedConnectorProperties.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/PredefinedConnectorProperties.java
new file mode 100644
index 000000000..ef0b84200
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/PredefinedConnectorProperties.java
@@ -0,0 +1,54 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.entities;
+
+import static org.libreplan.business.i18n.I18nHelper._;
+
+/**
+ * Simply class to keep constants of {@link ConnectorProperty properties} for
+ * LibrePlan {@link Connector connectors}.
+ *
+ * @author Miciele Ghiorghis
+ * @author Manuel Rego Casasnovas
+ */
+public class PredefinedConnectorProperties {
+
+ // Generic
+ public static String ACTIVATED = _("Activated");
+ public static String SERVER_URL = _("Server URL");
+ public static String USERNAME = _("Username");
+ public static String PASSWORD = _("Password");
+
+ // Specific for Tim
+ public static String TIM_NR_DAYS_TIMESHEET = _("Number of days timesheet to Tim");
+ public static String TIM_NR_DAYS_ROSTER = _("Number of days roster from Tim");
+ public static String TIM_PRODUCTIVITY_FACTOR = _("Productivity factor");
+ public static String TIM_DEPARTAMENTS_IMPORT_ROSTER = _("Department IDs to import toster");
+
+ // Specific for JIRA
+ public static String JIRA_LABELS = _("JIRA labels: comma-separated list of labels or URL");
+ public static String JIRA_HOURS_TYPE = _("Hours type");
+
+ /**
+ * Code prefix for different entities integrated with JIRA.
+ */
+ public static final String JIRA_CODE_PREFIX = "JIRA-";
+
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/common/entities/PredefinedConnectors.java b/libreplan-business/src/main/java/org/libreplan/business/common/entities/PredefinedConnectors.java
new file mode 100644
index 000000000..b51adc8a1
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/common/entities/PredefinedConnectors.java
@@ -0,0 +1,72 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.common.entities;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Defines the LibrePlan {@link Connector Connectors} together with its
+ * configuration properties.
+ *
+ * @author Miciele Ghiorghis
+ * @author Manuel Rego Casasnovas
+ */
+public enum PredefinedConnectors {
+
+ TIM("Tim",
+ ConnectorProperty.create(PredefinedConnectorProperties.ACTIVATED, "N"),
+ ConnectorProperty.create(PredefinedConnectorProperties.SERVER_URL, ""),
+ ConnectorProperty.create(PredefinedConnectorProperties.USERNAME, ""),
+ ConnectorProperty.create(PredefinedConnectorProperties.PASSWORD, ""),
+ ConnectorProperty.create(PredefinedConnectorProperties.TIM_NR_DAYS_TIMESHEET, "7"),
+ ConnectorProperty.create(PredefinedConnectorProperties.TIM_NR_DAYS_ROSTER, "90"),
+ ConnectorProperty.create(PredefinedConnectorProperties.TIM_PRODUCTIVITY_FACTOR, "100"),
+ ConnectorProperty.create(
+ PredefinedConnectorProperties.TIM_DEPARTAMENTS_IMPORT_ROSTER,
+ "0")),
+ JIRA("Jira",
+ ConnectorProperty.create(PredefinedConnectorProperties.ACTIVATED, "N"),
+ ConnectorProperty.create(PredefinedConnectorProperties.SERVER_URL, ""),
+ ConnectorProperty.create(PredefinedConnectorProperties.USERNAME, ""),
+ ConnectorProperty.create(PredefinedConnectorProperties.PASSWORD, ""),
+ ConnectorProperty
+ .create(PredefinedConnectorProperties.JIRA_LABELS, ""),
+ ConnectorProperty.create(
+ PredefinedConnectorProperties.JIRA_HOURS_TYPE, "Default"));
+
+ private String name;
+ private List properties;
+
+ private PredefinedConnectors(String name,
+ ConnectorProperty... properties) {
+ this.name = name;
+ this.properties = Arrays.asList(properties);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public List getProperties() {
+ return properties;
+ }
+
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/costcategories/daos/TypeOfWorkHoursDAO.java b/libreplan-business/src/main/java/org/libreplan/business/costcategories/daos/TypeOfWorkHoursDAO.java
index 59573128c..b98e9f1b5 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/costcategories/daos/TypeOfWorkHoursDAO.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/costcategories/daos/TypeOfWorkHoursDAO.java
@@ -29,8 +29,12 @@ import org.hibernate.Criteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.libreplan.business.common.daos.IConfigurationDAO;
+import org.libreplan.business.common.daos.IConnectorDAO;
import org.libreplan.business.common.daos.IntegrationEntityDAO;
import org.libreplan.business.common.entities.Configuration;
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.common.entities.PredefinedConnectorProperties;
+import org.libreplan.business.common.entities.PredefinedConnectors;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.common.exceptions.ValidationException;
import org.libreplan.business.costcategories.entities.HourCost;
@@ -56,6 +60,9 @@ public class TypeOfWorkHoursDAO extends IntegrationEntityDAO
@Autowired
private IConfigurationDAO configurationDAO;
+ @Autowired
+ private IConnectorDAO connectorDAO;
+
@Override
public TypeOfWorkHours findUniqueByCode(TypeOfWorkHours typeOfWorkHours)
throws InstanceNotFoundException {
@@ -194,13 +201,17 @@ public class TypeOfWorkHoursDAO extends IntegrationEntityDAO
}
private void checkIsJiraConnectorTypeOfWorkHours(TypeOfWorkHours type) {
- Configuration configuration = configurationDAO.getConfiguration();
- if (configuration.getJiraConfiguration()
- .getJiraConnectorTypeOfWorkHours().getId().equals(type.getId())) {
- throw ValidationException
- .invalidValue(
- "Cannot delete the type of work hours. It is configured as type of work hours for JIRA connector.",
- type);
+ Connector connector = connectorDAO
+ .findUniqueByName(PredefinedConnectors.JIRA.getName());
+ if (connector != null) {
+ String name = connector.getPropertiesAsMap().get(
+ PredefinedConnectorProperties.JIRA_HOURS_TYPE);
+ if (name.equals(type.getName())) {
+ throw ValidationException
+ .invalidValue(
+ "Cannot delete the type of work hours. It is configured as type of work hours for JIRA connector.",
+ type);
+ }
}
}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/costcategories/entities/TypeOfWorkHours.java b/libreplan-business/src/main/java/org/libreplan/business/costcategories/entities/TypeOfWorkHours.java
index f26d72b22..0f09a66c1 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/costcategories/entities/TypeOfWorkHours.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/costcategories/entities/TypeOfWorkHours.java
@@ -30,6 +30,9 @@ import org.hibernate.validator.NotNull;
import org.libreplan.business.common.IHumanIdentifiable;
import org.libreplan.business.common.IntegrationEntity;
import org.libreplan.business.common.Registry;
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.common.entities.PredefinedConnectorProperties;
+import org.libreplan.business.common.entities.PredefinedConnectors;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.costcategories.daos.ITypeOfWorkHoursDAO;
@@ -168,11 +171,13 @@ public class TypeOfWorkHours extends IntegrationEntity implements IHumanIdentifi
@AssertTrue(message = "type of work hours for JIRA connector cannot be disabled")
public boolean checkJiraConnectorTypeOfWorkHoursNotDisabled() {
if (!isNewObject() && !getEnabled()) {
- TypeOfWorkHours typeOfWorkHours = Registry.getConfigurationDAO()
- .getConfiguration().getJiraConfiguration()
- .getJiraConnectorTypeOfWorkHours();
- if (typeOfWorkHours.getId().equals(getId())) {
- return false;
+ Connector connector = Registry.getConnectorDAO().findUniqueByName(
+ PredefinedConnectors.JIRA.getName());
+ if (connector != null) {
+ if (this.name.equals(connector.getPropertiesAsMap().get(
+ PredefinedConnectorProperties.JIRA_HOURS_TYPE))) {
+ return false;
+ }
}
}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/IOrderSyncInfoDAO.java b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/IOrderSyncInfoDAO.java
new file mode 100644
index 000000000..d81d7078f
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/IOrderSyncInfoDAO.java
@@ -0,0 +1,111 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.orders.daos;
+
+import java.util.List;
+
+import org.libreplan.business.common.daos.IGenericDAO;
+import org.libreplan.business.orders.entities.Order;
+import org.libreplan.business.orders.entities.OrderSyncInfo;
+
+/**
+ * Contract for {@link OrderSyncInfoDAO}
+ *
+ * @author Miciele Ghiorghis
+ */
+public interface IOrderSyncInfoDAO extends IGenericDAO {
+
+ /**
+ * Search last synchronized info for the specified
+ * {@link Order} and connectorName
+ *
+ * @param order
+ * the order to search for
+ * @param connectorName
+ * the connector name
+ *
+ * @return Last synchronized info
+ */
+ OrderSyncInfo findLastSynchronizedInfoByOrderAndConnectorName(Order order,
+ String connectorName);
+
+ /**
+ * Search last synchronized infos for the specified
+ * {@link Order} and connectorName
+ *
+ * @param order
+ * the order to search for
+ * @param connectorName
+ * the connector name
+ * @return list of last synchronized infos
+ */
+ List findLastSynchronizedInfosByOrderAndConnectorName(
+ Order order, String connectorName);
+
+ /**
+ * Searches and returns {@link OrderSyncInfo} for the specified
+ * key and connectorName
+ *
+ * @param key
+ * the unique key with in connector id
+ * @param order
+ * the order
+ * @param connectorName
+ * the connector name
+ */
+ OrderSyncInfo findByKeyOrderAndConnectorName(String key, Order order,
+ String connectorName);
+
+ /**
+ * Finds the {@link OrderSyncInfo}s for the specified
+ * connectorName
+ *
+ * @param connectorName
+ * the connector name
+ * @return a list of OrderSyncInfo if found and null if not
+ */
+ List findByConnectorName(String connectorName);
+
+ /**
+ * Returns true if there exists other {@link OrderSyncInfo} with the same
+ * {@link OrderSyncInfo#getKey()},
+ * {@link OrderSyncInfo#getOrder()} and
+ * {@link OrderSyncInfo#getConnectorName()}
+ *
+ * @param orderSyncInfo
+ * the {@link OrderSyncInfo}
+ */
+ boolean existsByKeyOrderAndConnectorNameAnotherTransaction(
+ OrderSyncInfo orderSyncInfo);
+
+ /**
+ * Returns unique {@link OrderSyncInfo} for the specified key,
+ * order and connectorName
+ *
+ * @param key
+ * the key
+ * @param order
+ * an order
+ * @param connectorName
+ * the name of the connector
+ */
+ OrderSyncInfo findUniqueByKeyOrderAndConnectorNameAnotherTransaction(
+ String key, Order order, String connectorName);
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderDAO.java b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderDAO.java
index 9de3542a0..f8e563dc9 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderDAO.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderDAO.java
@@ -442,12 +442,16 @@ public class OrderDAO extends IntegrationEntityDAO implements
} else {
String strQuery = "SELECT oa.order.id "
+ "FROM OrderAuthorization oa "
- + "WHERE oa.user = :user "
- + "OR oa.profile IN (:profiles) ";
+ + "WHERE oa.user = :user ";
+ if (!user.getProfiles().isEmpty()) {
+ strQuery += "OR oa.profile IN (:profiles) ";
+ }
Query query = getSession().createQuery(strQuery);
query.setParameter("user", user);
- query.setParameterList("profiles", user.getProfiles());
+ if (!user.getProfiles().isEmpty()) {
+ query.setParameterList("profiles", user.getProfiles());
+ }
return query.list();
}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderSyncInfoDAO.java b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderSyncInfoDAO.java
new file mode 100644
index 000000000..eb4c916bf
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/orders/daos/OrderSyncInfoDAO.java
@@ -0,0 +1,113 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.orders.daos;
+
+import java.util.List;
+
+import org.hibernate.Criteria;
+import org.hibernate.criterion.Restrictions;
+import org.libreplan.business.common.daos.GenericDAOHibernate;
+import org.libreplan.business.orders.entities.Order;
+import org.libreplan.business.orders.entities.OrderSyncInfo;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Propagation;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * DAO for {@link OrderSyncInfo}
+ *
+ * @author Miciele Ghiorghis
+ */
+@Repository
+@Scope(BeanDefinition.SCOPE_SINGLETON)
+public class OrderSyncInfoDAO extends GenericDAOHibernate
+ implements IOrderSyncInfoDAO {
+
+ @Override
+ public OrderSyncInfo findLastSynchronizedInfoByOrderAndConnectorName(
+ Order order, String connectorName) {
+ List orderSyncInfoList = findLastSynchronizedInfosByOrderAndConnectorName(
+ order, connectorName);
+ if (orderSyncInfoList == null || orderSyncInfoList.isEmpty()) {
+ return null;
+ }
+ return orderSyncInfoList.get(0);
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public List findLastSynchronizedInfosByOrderAndConnectorName(
+ Order order, String connectorName) {
+ Criteria criteria = getSession().createCriteria(OrderSyncInfo.class);
+ criteria.add(Restrictions.eq("order", order));
+ criteria.add(Restrictions.eq("connectorName", connectorName));
+ criteria.addOrder(org.hibernate.criterion.Order.desc("lastSyncDate"));
+ return criteria.list();
+ }
+
+ @Override
+ public OrderSyncInfo findByKeyOrderAndConnectorName(String key,
+ Order order, String connectorName) {
+ Criteria criteria = getSession().createCriteria(OrderSyncInfo.class);
+ criteria.add(Restrictions.eq("key", key));
+ criteria.add(Restrictions.eq("order", order));
+ criteria.add(Restrictions.eq("connectorName", connectorName));
+ return (OrderSyncInfo) criteria.uniqueResult();
+ }
+
+ @Override
+ public List findByConnectorName(String connectorName) {
+ Criteria criteria = getSession().createCriteria(OrderSyncInfo.class);
+ criteria.add(Restrictions.eq("connectorName", connectorName));
+ return criteria.list();
+ }
+
+ @Override
+ @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+ public boolean existsByKeyOrderAndConnectorNameAnotherTransaction(
+ OrderSyncInfo orderSyncInfo) {
+ return existsOtherOrderSyncInfoByKeyOrderAndConnectorName(orderSyncInfo);
+ }
+
+ /**
+ * Returns true if other {@link OrderSyncInfo} which is the same
+ * as the given {@link OrderSyncInfo} already exists
+ *
+ * @param orderSyncInfo
+ * the {@link OrderSyncInfo}
+ */
+ private boolean existsOtherOrderSyncInfoByKeyOrderAndConnectorName(
+ OrderSyncInfo orderSyncInfo) {
+ OrderSyncInfo found = findByKeyOrderAndConnectorName(
+ orderSyncInfo.getKey(), orderSyncInfo.getOrder(),
+ orderSyncInfo.getConnectorName());
+ return found != null && found != orderSyncInfo;
+ }
+
+ @Override
+ @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
+ public OrderSyncInfo findUniqueByKeyOrderAndConnectorNameAnotherTransaction(
+ String key, Order order, String connectorName) {
+ return findByKeyOrderAndConnectorName(key, order, connectorName);
+ }
+
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/Order.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/Order.java
index c2436a5a9..2cc90525e 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/Order.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/Order.java
@@ -120,8 +120,6 @@ public class Order extends OrderLineGroup implements Comparable {
private Set customerCommunications = new HashSet();
- private String importedLabel;
-
@Valid
private SortedSet deliveringDates = new TreeSet(
new DeliverDateComparator());
@@ -693,14 +691,6 @@ public class Order extends OrderLineGroup implements Comparable {
return true;
}
- public String getImportedLabel() {
- return importedLabel;
- }
-
- public void setImportedLabel(String importedLabel) {
- this.importedLabel = importedLabel;
- }
-
public void calculateAndSetTotalHours() {
int result = 0;
for (OrderElement orderElement : this.getChildren()) {
diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java
index 73fabd95e..367ec5bce 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderElement.java
@@ -52,7 +52,7 @@ import org.libreplan.business.advance.exceptions.DuplicateValueTrueReportGlobalA
import org.libreplan.business.common.IntegrationEntity;
import org.libreplan.business.common.Registry;
import org.libreplan.business.common.daos.IIntegrationEntityDAO;
-import org.libreplan.business.common.entities.JiraConfiguration;
+import org.libreplan.business.common.entities.PredefinedConnectorProperties;
import org.libreplan.business.common.exceptions.ValidationException;
import org.libreplan.business.labels.entities.Label;
import org.libreplan.business.materials.entities.MaterialAssignment;
@@ -1568,6 +1568,24 @@ public abstract class OrderElement extends IntegrationEntity implements
return workReportLineDAO.findByOrderElementAndChildren(this, sortedByDate);
}
+ /**
+ * Gets workReportLines of this order-element between the specified
+ * startDate and endDate
+ *
+ * @param startDate
+ * the startDate
+ * @param endDate
+ * the endDate
+ * @param sortedByDate
+ * @return list of workReportLines
+ */
+ public List getWorkReportLines(Date startDate,
+ Date endDate, boolean sortedByDate) {
+ IWorkReportLineDAO workReportLineDAO = Registry.getWorkReportLineDAO();
+ return workReportLineDAO.findByOrderElementAndChildrenFilteredByDate(
+ this, startDate, endDate, sortedByDate);
+ }
+
/**
* Checks if it has nay consolidated advance, if not checks if any parent
* has it
@@ -1668,7 +1686,7 @@ public abstract class OrderElement extends IntegrationEntity implements
if (code == null) {
return false;
}
- return code.startsWith(JiraConfiguration.CODE_PREFIX);
+ return code.startsWith(PredefinedConnectorProperties.JIRA_CODE_PREFIX);
}
public boolean isConvertedToContainer() {
diff --git a/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderSyncInfo.java b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderSyncInfo.java
new file mode 100644
index 000000000..3cec95fec
--- /dev/null
+++ b/libreplan-business/src/main/java/org/libreplan/business/orders/entities/OrderSyncInfo.java
@@ -0,0 +1,130 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.business.orders.entities;
+
+import java.util.Date;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.lang.Validate;
+import org.hibernate.validator.AssertTrue;
+import org.hibernate.validator.NotNull;
+import org.libreplan.business.common.BaseEntity;
+import org.libreplan.business.common.Registry;
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.orders.daos.IOrderSyncInfoDAO;
+
+/**
+ * OrderSyncInfo entity. This entity holds order synchronization info. Each time
+ * that order synchronization is successfully completed, an instance of this
+ * entity is created or updated and saved to DB to hold the synchronized info.
+ * This info is then displayed in UI.
+ *
+ * This entity contains the following fields:
+ *
+ * - lastSyncDate: last date where synchronization took place
+ * - key: an identifier, which connector's key is last synchronized
+ * - connectorName: the name of the {@link Connector} that has running the
+ * synchronization
+ * - order: order that is synchronized
+ *
+ *
+ * @author Miciele Ghiorghis
+ */
+public class OrderSyncInfo extends BaseEntity {
+
+ private Date lastSyncDate;
+ private String key;
+ private String connectorName;
+ private Order order;
+
+ public static OrderSyncInfo create(String key, Order order,
+ String connectorName) {
+ Validate.notEmpty(key);
+ Validate.notNull(order);
+ Validate.notEmpty(connectorName);
+ return create(new OrderSyncInfo(key, order, connectorName));
+ }
+
+ /**
+ * Constructor for Hibernate. Do not use!
+ */
+ protected OrderSyncInfo() {
+ }
+
+ private OrderSyncInfo(String key, Order order, String connectorName) {
+ this.lastSyncDate = new Date();
+ this.key = key;
+ this.order = order;
+ this.connectorName = connectorName;
+ }
+
+ @NotNull(message = "last synchronized date not specified")
+ public Date getLastSyncDate() {
+ return lastSyncDate;
+ }
+
+ public void setLastSyncDate(Date lastSyncDate) {
+ this.lastSyncDate = lastSyncDate;
+ }
+
+ @NotNull(message = "key not specified")
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ @NotNull(message = "connector name not specified")
+ public String getConnectorName() {
+ return connectorName;
+ }
+
+ public void setConnectorName(String connectorName) {
+ this.connectorName = connectorName;
+ }
+
+ public Order getOrder() {
+ return order;
+ }
+
+ public void setOrder(Order order) {
+ this.order = order;
+ }
+
+ @AssertTrue(message = "project sync info is already being used")
+ public boolean checkConstraintUniqueOrderSyncInfo() {
+ if (StringUtils.isBlank(key) && order == null
+ && StringUtils.isBlank(connectorName)) {
+ return true;
+ }
+ IOrderSyncInfoDAO orderSyncInfoDAO = Registry.getOrderSyncInfoDAO();
+ if (isNewObject()) {
+ return !orderSyncInfoDAO
+ .existsByKeyOrderAndConnectorNameAnotherTransaction(this);
+ } else {
+ OrderSyncInfo found = orderSyncInfoDAO
+ .findUniqueByKeyOrderAndConnectorNameAnotherTransaction(
+ key, order, connectorName);
+ return found == null || found.getId().equals(getId());
+ }
+ }
+}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/users/bootstrap/PredefinedProfiles.java b/libreplan-business/src/main/java/org/libreplan/business/users/bootstrap/PredefinedProfiles.java
index 897e321e0..b7c0f7fee 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/users/bootstrap/PredefinedProfiles.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/users/bootstrap/PredefinedProfiles.java
@@ -29,6 +29,7 @@ import static org.libreplan.business.users.entities.UserRole.ROLE_ESTIMATED_PLAN
import static org.libreplan.business.users.entities.UserRole.ROLE_EXPENSES;
import static org.libreplan.business.users.entities.UserRole.ROLE_HOURS_TYPES;
import static org.libreplan.business.users.entities.UserRole.ROLE_HOURS_WORKED_PER_RESOURCE_REPORT;
+import static org.libreplan.business.users.entities.UserRole.ROLE_JOB_SCHEDULING;
import static org.libreplan.business.users.entities.UserRole.ROLE_LABELS;
import static org.libreplan.business.users.entities.UserRole.ROLE_MACHINES;
import static org.libreplan.business.users.entities.UserRole.ROLE_MAIN_SETTINGS;
@@ -74,7 +75,7 @@ import org.libreplan.business.users.entities.UserRole;
public enum PredefinedProfiles {
SYSTEMS_ADMINISTRATOR("Systems Administrator", ROLE_MAIN_SETTINGS,
- ROLE_USER_ACCOUNTS, ROLE_PROFILES),
+ ROLE_USER_ACCOUNTS, ROLE_PROFILES, ROLE_JOB_SCHEDULING),
PROJECT_MANAGER("Project Manager", ROLE_READ_ALL_PROJECTS,
ROLE_EDIT_ALL_PROJECTS, ROLE_CREATE_PROJECTS, ROLE_PLANNING,
diff --git a/libreplan-business/src/main/java/org/libreplan/business/users/entities/UserRole.java b/libreplan-business/src/main/java/org/libreplan/business/users/entities/UserRole.java
index d6ee74b29..20495916d 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/users/entities/UserRole.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/users/entities/UserRole.java
@@ -66,6 +66,7 @@ public enum UserRole {
ROLE_MAIN_SETTINGS(_("Main Settings")),
ROLE_USER_ACCOUNTS(_("User Accounts")),
ROLE_PROFILES(_("Profiles")),
+ ROLE_JOB_SCHEDULING(_("Job Scheduling")),
ROLE_COMPANIES(_("Companies")),
ROLE_SEND_TO_SUBCONTRACTORS(_("Send To Subcontractors")),
ROLE_RECEIVED_FROM_SUBCONTRACTORS(_("Received From Subcontractors")),
diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java
index 39f8ddd1f..b05924bcc 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/IWorkReportLineDAO.java
@@ -76,5 +76,12 @@ public interface IWorkReportLineDAO extends
List findByOrderElementAndWorkReports(
OrderElement orderElement, List workReports);
+ /**
+ * Returns the {@link WorkReportLine}s of the specified
+ * orderElement specified between start date and
+ * end date
+ */
+ List findByOrderElementAndChildrenFilteredByDate(
+ OrderElement orderElement, Date start, Date end, boolean sortByDate);
}
diff --git a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java
index 153e707c2..371aea2c3 100644
--- a/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java
+++ b/libreplan-business/src/main/java/org/libreplan/business/workreports/daos/WorkReportLineDAO.java
@@ -216,4 +216,33 @@ public class WorkReportLineDAO extends IntegrationEntityDAO
return (List) criteria.list();
}
+ @Transactional(readOnly = true)
+ public List findByOrderElementAndChildrenFilteredByDate(
+ OrderElement orderElement, Date start, Date end, boolean sortByDate) {
+
+ if (orderElement.isNewObject()) {
+ return new ArrayList();
+ }
+
+ Collection orderElements = orderElement.getAllChildren();
+ orderElements.add(orderElement);
+
+ // Prepare criteria
+ final Criteria criteria = getSession().createCriteria(
+ WorkReportLine.class);
+ criteria.add(Restrictions.in("orderElement", orderElements));
+
+ if (start != null) {
+ criteria.add(Restrictions.ge("date", start));
+ }
+ if (end != null) {
+ criteria.add(Restrictions.le("date", end));
+ }
+ if (sortByDate) {
+ criteria.addOrder(org.hibernate.criterion.Order.asc("date"));
+ }
+ return criteria.list();
+
+ }
+
}
diff --git a/libreplan-business/src/main/resources/db.changelog-1.3.xml b/libreplan-business/src/main/resources/db.changelog-1.3.xml
index 72aa5c6a6..6221e8db3 100644
--- a/libreplan-business/src/main/resources/db.changelog-1.3.xml
+++ b/libreplan-business/src/main/resources/db.changelog-1.3.xml
@@ -221,106 +221,100 @@
columnDataType="INTEGER" />
-
-
-
- Add new column jira_activated with default value FALSE to configuration table
-
-
-
-
-
-
+
+
+ Create new table order_sync_info
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- Add new column jira_url in table configuration
-
-
-
-
-
+
+
+ Create tables related to Connector entity
+
+
+
+
+
+
+
+
+
+
+
-
-
- Add new column jira_label_url in table configuration
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- Add new column jira_user_id in table configuration
-
-
-
-
-
+
-
-
- Add new column jira_user_id in table configuration
-
-
-
-
-
-
-
-
- Add new column imported_label in table order_table
-
-
-
-
-
-
-
-
- Add new column jira_connector_type_of_work_hours to configuration
- table.
-
-
-
-
+ baseColumnNames="connector_id"
+ baseTableName="connector_property"
+ constraintName="connector_property_connector_id_fkey"
+ deferrable="false" initiallyDeferred="false"
+ onDelete="NO ACTION" onUpdate="NO ACTION"
+ referencedColumnNames="id"
+ referencedTableName="connector"
+ referencesUniqueColumn="false"/>
-
-
-
- Rename column jira_label_url to jira_labels in configuration table
-
-
-
-
-
- Change column jira_labels in configuration to TEXT
-
-
-
-
- Change column jira_labels in configuration to TEXT in MySQL
- ALTER TABLE configuration MODIFY jira_labels TEXT
+
+
+ Create new table job_scheduler_configuration
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/libreplan-business/src/main/resources/libreplan-business-spring-config.xml b/libreplan-business/src/main/resources/libreplan-business-spring-config.xml
index 10dda88a4..e5e183a1b 100644
--- a/libreplan-business/src/main/resources/libreplan-business-spring-config.xml
+++ b/libreplan-business/src/main/resources/libreplan-business-spring-config.xml
@@ -90,7 +90,13 @@
org/libreplan/business/expensesheets/entities/ExpenseSheets.hbm.xml
-
+
+ org/libreplan/business/common/entities/Connector.hbm.xml
+
+
+ org/libreplan/business/common/entities/JobSchedulerConfiguration.hbm.xml
+
+
-
-
-
@@ -300,4 +297,20 @@
+
+
+
+ 100
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libreplan-webapp/pom.xml b/libreplan-webapp/pom.xml
index 95cd2e81b..5503136b6 100644
--- a/libreplan-webapp/pom.xml
+++ b/libreplan-webapp/pom.xml
@@ -330,6 +330,11 @@
org.springframework
spring-test
+
+
+ org.springframework
+ spring-context-support
+
org.springframework.security
@@ -481,5 +486,10 @@
commons-io
commons-io
+
+
+ org.quartz-scheduler
+ quartz
+
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetToTimJob.java b/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetToTimJob.java
new file mode 100644
index 000000000..b5add4f97
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetToTimJob.java
@@ -0,0 +1,62 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers;
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.libreplan.business.common.entities.ConnectorException;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+
+/**
+ * A job that exports time sheets to Tim SOAP server
+ *
+ * @author Miciele Ghiorghis
+ */
+public class ExportTimesheetToTimJob extends QuartzJobBean {
+ private static final Log LOG = LogFactory
+ .getLog(ExportTimesheetToTimJob.class);
+
+ @Override
+ protected void executeInternal(JobExecutionContext context)
+ throws JobExecutionException {
+ ApplicationContext applicationContext = (ApplicationContext) context
+ .getJobDetail().getJobDataMap().get("applicationContext");
+
+ IExportTimesheetsToTim exportTimesheetsToTim = (IExportTimesheetsToTim) applicationContext
+ .getBean("exportTimesheetsToTim");
+
+ try {
+ List syncInfos = exportTimesheetsToTim
+ .exportTimesheets();
+
+ LOG.info("Export scuccessful: "
+ + (syncInfos == null || syncInfos.isEmpty()));
+
+ } catch (ConnectorException e) {
+ LOG.error("Export timesheet to Tim failed", e);
+ }
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetsToTim.java b/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetsToTim.java
new file mode 100644
index 000000000..8ad2a329d
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/ExportTimesheetsToTim.java
@@ -0,0 +1,341 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers;
+
+import static org.libreplan.web.I18nHelper._;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.joda.time.LocalDate;
+import org.libreplan.business.common.IAdHocTransactionService;
+import org.libreplan.business.common.IOnTransaction;
+import org.libreplan.business.common.daos.IConnectorDAO;
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.common.entities.ConnectorException;
+import org.libreplan.business.common.entities.PredefinedConnectorProperties;
+import org.libreplan.business.common.entities.PredefinedConnectors;
+import org.libreplan.business.common.exceptions.InstanceNotFoundException;
+import org.libreplan.business.orders.daos.IOrderSyncInfoDAO;
+import org.libreplan.business.orders.entities.Order;
+import org.libreplan.business.orders.entities.OrderSyncInfo;
+import org.libreplan.business.resources.daos.IWorkerDAO;
+import org.libreplan.business.resources.entities.Worker;
+import org.libreplan.business.workreports.entities.WorkReportLine;
+import org.libreplan.importers.tim.DurationDTO;
+import org.libreplan.importers.tim.PersonDTO;
+import org.libreplan.importers.tim.ProductDTO;
+import org.libreplan.importers.tim.RegistrationDateDTO;
+import org.libreplan.importers.tim.TimOptions;
+import org.libreplan.importers.tim.TimeRegistrationDTO;
+import org.libreplan.importers.tim.TimeRegistrationRequestDTO;
+import org.libreplan.importers.tim.TimeRegistrationResponseDTO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Implementation of export timesheets to tim
+ *
+ * @author Miciele Ghiorghis
+ */
+@Component
+@Scope(BeanDefinition.SCOPE_PROTOTYPE)
+public class ExportTimesheetsToTim implements IExportTimesheetsToTim {
+
+ private static final Log LOG = LogFactory
+ .getLog(ExportTimesheetsToTim.class);
+
+ @Autowired
+ private IWorkerDAO workerDAO;
+
+ @Autowired
+ IOrderSyncInfoDAO orderSyncInfoDAO;
+
+ @Autowired
+ private IAdHocTransactionService adHocTransactionService;
+
+ @Autowired
+ private IConnectorDAO connectorDAO;
+
+ private SynchronizationInfo synchronizationInfo;
+
+ @Override
+ @Transactional(readOnly = true)
+ public List exportTimesheets() throws ConnectorException {
+ Connector connector = getTimConnector();
+ if (connector == null) {
+ throw new ConnectorException(_("Tim connector not found"));
+ }
+ if (!connector.areConnectionValuesValid()) {
+ throw new ConnectorException(
+ _("Connection values of Tim connector are invalid"));
+ }
+
+ synchronizationInfo = new SynchronizationInfo(_("Export"));
+
+ List syncInfos = new ArrayList();
+
+ List orderSyncInfos = orderSyncInfoDAO.findByConnectorName(PredefinedConnectors.TIM.getName());
+ if (orderSyncInfos == null || orderSyncInfos.isEmpty()) {
+ LOG.warn("No items found in 'OrderSyncInfo' to export to Tim");
+ synchronizationInfo.addFailedReason(_("No items found in 'OrderSyncInfo' to export to Tim"));
+ syncInfos.add(synchronizationInfo);
+ return syncInfos;
+ }
+
+ for (OrderSyncInfo orderSyncInfo : orderSyncInfos) {
+ LOG.info("Exporting '" + orderSyncInfo.getOrder().getName() + "'");
+ exportTimesheets(orderSyncInfo.getKey(), orderSyncInfo.getOrder(),
+ connector);
+ if (!synchronizationInfo.isSuccessful()) {
+ syncInfos.add(synchronizationInfo);
+ }
+ }
+ return syncInfos;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public void exportTimesheets(String productCode, Order order)
+ throws ConnectorException {
+ if (productCode == null || productCode.isEmpty()) {
+ throw new ConnectorException(_("Product code should not be empty"));
+ }
+ if (order == null) {
+ throw new ConnectorException(_("Order should not be empty"));
+ }
+
+ Connector connector = getTimConnector();
+ if (connector == null) {
+ throw new ConnectorException(_("Tim connector not found"));
+ }
+
+ if (!connector.areConnectionValuesValid()) {
+ throw new ConnectorException(
+ _("Connection values of Tim connector are invalid"));
+ }
+
+ exportTimesheets(productCode, order, connector);
+ }
+
+ /**
+ * exports time sheets to Tim
+ *
+ * @param productCode
+ * the product code
+ * @param order
+ * the order
+ * @param connector
+ * the connector
+ *
+ * @return true if export is succeeded, false otherwise
+ */
+ private void exportTimesheets(String productCode, Order order,
+ Connector connector) {
+
+ synchronizationInfo = new SynchronizationInfo(_(
+ "Export product code {0}, project {1}", productCode,
+ order.getName()));
+
+ Map properties = connector.getPropertiesAsMap();
+
+ String url = properties.get(PredefinedConnectorProperties.SERVER_URL);
+ String userName = properties
+ .get(PredefinedConnectorProperties.USERNAME);
+ String password = properties
+ .get(PredefinedConnectorProperties.PASSWORD);
+ int nrDaysTimesheetToTim = Integer.parseInt(properties
+ .get(PredefinedConnectorProperties.TIM_NR_DAYS_TIMESHEET));
+
+ LocalDate dateNrOfDaysBack = new LocalDate()
+ .minusDays(nrDaysTimesheetToTim);
+
+ List workReportLines = order.getWorkReportLines(
+ dateNrOfDaysBack.toDateTimeAtStartOfDay().toDate(), new Date(),
+ true);
+ if (workReportLines == null || workReportLines.isEmpty()) {
+ LOG.warn("No work reportlines are found for order: '"
+ + order.getName() + "'");
+ synchronizationInfo.addFailedReason(_(
+ "No work reportlines are found for order: \"{0}\"",
+ order.getName()));
+ return;
+ }
+
+ List timeRegistrationDTOs = new ArrayList();
+
+ for (WorkReportLine workReportLine : workReportLines) {
+ TimeRegistrationDTO timeRegistrationDTO = createExportTimeRegistration(
+ productCode, workReportLine);
+ if (timeRegistrationDTO != null) {
+ timeRegistrationDTOs.add(timeRegistrationDTO);
+ }
+ }
+
+ if (timeRegistrationDTOs.isEmpty()) {
+ LOG.warn("Unable to crate timeregistration for request");
+ synchronizationInfo
+ .addFailedReason(_("Unable to crate time registration for request"));
+ return;
+ }
+
+ TimeRegistrationRequestDTO timeRegistrationRequestDTO = new TimeRegistrationRequestDTO();
+ timeRegistrationRequestDTO.setTimeRegistrations(timeRegistrationDTOs);
+
+ TimeRegistrationResponseDTO timeRegistrationResponseDTO = TimSoapClient
+ .sendRequestReceiveResponse(url, userName, password,
+ timeRegistrationRequestDTO, TimeRegistrationResponseDTO.class);
+
+ if (timeRegistrationResponseDTO == null) {
+ LOG.error("No response or exception in response");
+ synchronizationInfo
+ .addFailedReason(_("No response or exception in response"));
+ return;
+ }
+
+ if (isRefsListEmpty(timeRegistrationResponseDTO.getRefs())) {
+ LOG.warn("Registration response with empty refs");
+ synchronizationInfo
+ .addFailedReason(_("Registration response with empty refs"));
+ return;
+ }
+ saveSyncInfoOnAnotherTransaction(productCode, order);
+ }
+
+ /**
+ * checks if list of refs is empty
+ *
+ * @param refs
+ * the list of refs
+ * @return true if list is empty otherwise false
+ */
+ private boolean isRefsListEmpty(List refs) {
+ if (refs == null) {
+ return true;
+ }
+ refs.removeAll(Collections.singleton(0));
+ return refs.isEmpty();
+ }
+
+ /**
+ * Saves synchronization info
+ *
+ * @param productCode
+ * the productcode
+ * @param order
+ * the order
+ */
+ private void saveSyncInfoOnAnotherTransaction(final String productCode,
+ final Order order) {
+ adHocTransactionService
+ .runOnAnotherTransaction(new IOnTransaction() {
+ @Override
+ public Void execute() {
+ OrderSyncInfo orderSyncInfo = orderSyncInfoDAO
+ .findByKeyOrderAndConnectorName(productCode,
+ order,
+ PredefinedConnectors.TIM.getName());
+ if (orderSyncInfo == null) {
+ orderSyncInfo = OrderSyncInfo.create(productCode,
+ order, PredefinedConnectors.TIM.getName());
+ }
+ orderSyncInfo.setLastSyncDate(new Date());
+ orderSyncInfoDAO.save(orderSyncInfo);
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Creates export time registration
+ *
+ * @param productCode
+ * the product code
+ * @param workReportLine
+ * the workreportLine
+ * @return timeRegistration DTO
+ */
+ private TimeRegistrationDTO createExportTimeRegistration(String productCode,
+ WorkReportLine workReportLine) {
+ Worker worker;
+ String workerCode = workReportLine.getResource().getCode();
+ try {
+ worker = workerDAO.findByCode(workerCode);
+ } catch (InstanceNotFoundException e) {
+ LOG.warn("Worker '" + workerCode + "' not found");
+ synchronizationInfo.addFailedReason(_("Worker \"{0}\" not found",
+ workerCode));
+ return null;
+ }
+
+ PersonDTO personDTO = new PersonDTO();
+ personDTO.setName(worker.getName());
+ personDTO.setOptions(TimOptions.UPDATE_OR_INSERT);
+
+ ProductDTO productDTO = new ProductDTO();
+ productDTO.setOptions(TimOptions.UPDATE_OR_INSERT);
+ productDTO.setCode(productCode);
+
+ RegistrationDateDTO registrationDTO = new RegistrationDateDTO();
+ registrationDTO.setOptions(TimOptions.UPDATE_OR_INSERT);
+ registrationDTO.setDate(workReportLine.getLocalDate());
+
+ DurationDTO durationDTO = new DurationDTO();
+ durationDTO.setOptions(TimOptions.DECIMAL);
+ durationDTO.setDuration(workReportLine.getEffort()
+ .toHoursAsDecimalWithScale(2).doubleValue());
+
+ TimeRegistrationDTO timeRegistrationDTO = new TimeRegistrationDTO();
+ timeRegistrationDTO.setPerson(personDTO);
+ timeRegistrationDTO.setProduct(productDTO);
+ timeRegistrationDTO.setRegistrationDate(registrationDTO);
+ timeRegistrationDTO.setDuration(durationDTO);
+ return timeRegistrationDTO;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public OrderSyncInfo getOrderLastSyncInfo(Order order) {
+ return orderSyncInfoDAO.findLastSynchronizedInfoByOrderAndConnectorName(
+ order, PredefinedConnectors.TIM.getName());
+ }
+
+ /**
+ * finds and returns a Tim connector
+ */
+ private Connector getTimConnector() {
+ return connectorDAO
+ .findUniqueByName(PredefinedConnectors.TIM.getName());
+ }
+
+ @Override
+ public SynchronizationInfo getSynchronizationInfo() {
+ return synchronizationInfo;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/IExportTimesheetsToTim.java b/libreplan-webapp/src/main/java/org/libreplan/importers/IExportTimesheetsToTim.java
new file mode 100644
index 000000000..15945e12d
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/IExportTimesheetsToTim.java
@@ -0,0 +1,83 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers;
+
+import java.util.List;
+
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.common.entities.ConnectorException;
+import org.libreplan.business.orders.entities.Order;
+import org.libreplan.business.orders.entities.OrderSyncInfo;
+
+/**
+ * Export time sheets of an existing order to Tim SOAP server using
+ * {@link TimSoapClient}.
+ *
+ * It exports the time sheets between periods current-date minus
+ * NrDaysTimesheetToTim specified in the Tim {@link Connector} and
+ * the current-date
+ *
+ * @author Miciele Ghiorghis
+ */
+public interface IExportTimesheetsToTim {
+
+ /**
+ * Exports time sheets of the specified productCode and
+ * {@link Order} to Tim SOAP server
+ *
+ * @param productCode
+ * the Tim's productCode
+ * @param order
+ * an existing order
+ * @throws ConnectorException
+ * if connector is not valid
+ */
+ void exportTimesheets(String productCode, Order order) throws ConnectorException;
+
+ /**
+ * Exporting the time sheets to Tim SOAP server, if they are already
+ * exported using
+ * {@link IExportTimesheetsToTim#exportTimesheets(String, Order)}.
+ *
+ * It gets then an already exported time sheets from {@link OrderSyncInfo}
+ * and re-exporting them.
+ *
+ * @return a list of {@link SynchronizationInfo}
+ *
+ * @throws ConnectorException
+ * if connector is not valid
+ */
+ List exportTimesheets() throws ConnectorException;
+
+ /**
+ * Gets the most recent synchronized time sheet info
+ *
+ * @param order
+ * the order
+ * @return recent synchronized time sheet info
+ */
+ OrderSyncInfo getOrderLastSyncInfo(Order order);
+
+ /**
+ * Returns synchronization info, success of fail info
+ */
+ SynchronizationInfo getSynchronizationInfo();
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/IImportRosterFromTim.java b/libreplan-webapp/src/main/java/org/libreplan/importers/IImportRosterFromTim.java
new file mode 100644
index 000000000..82aabbf69
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/IImportRosterFromTim.java
@@ -0,0 +1,56 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers;
+
+import java.util.List;
+
+import org.libreplan.business.calendars.entities.CalendarException;
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.common.entities.ConnectorException;
+
+/**
+ * Import Rosters from Tim SOAP server using {@link TimSoapClient} and updates
+ * worker's Exception calendar accordingly
+ *
+ * It imports the Rosters between periods current-date and current-date plus
+ * NrDaysRosterFromTim specified in Tim {@link Connector}.
+ *
+ * @author Miciele Ghiorghis
+ */
+public interface IImportRosterFromTim {
+
+ /**
+ * Import rosters from Tim and update workers {@link CalendarException}
+ *
+ * If worker calendar exception already exists it will be removed and added
+ * new one, in other cases a new calendar exception will be created
+ *
+ * @return a list of {@link SynchronizationInfo}
+ *
+ * @throws ConnectorException
+ * if connector is not valid
+ */
+ List importRosters() throws ConnectorException;
+
+ /**
+ * Returns synchronization info, success of fail info
+ */
+ SynchronizationInfo getSynchronizationInfo();
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraOrderElementSynchronizer.java b/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraOrderElementSynchronizer.java
index 4f643118e..04a7b5711 100755
--- a/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraOrderElementSynchronizer.java
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraOrderElementSynchronizer.java
@@ -23,8 +23,10 @@ import java.util.List;
import org.libreplan.business.advance.entities.AdvanceMeasurement;
import org.libreplan.business.advance.entities.DirectAdvanceAssignment;
+import org.libreplan.business.common.entities.ConnectorException;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.orders.entities.OrderElement;
+import org.libreplan.business.orders.entities.OrderSyncInfo;
import org.libreplan.importers.jira.IssueDTO;
/**
@@ -48,8 +50,10 @@ public interface IJiraOrderElementSynchronizer {
* https://jira.atlassian.com/browse/JRA-29409
*
* @return A list of labels
+ * @throws ConnectorException
+ * if connector not found
*/
- List getAllJiraLabels();
+ List getAllJiraLabels() throws ConnectorException;
/**
* Get all jira issues based on the specified label parameter
@@ -59,8 +63,10 @@ public interface IJiraOrderElementSynchronizer {
* search criteria for jira issues
*
* @return list of jira issues
+ * @throws ConnectorException
+ * if connector not found or contains invalid connection values
*/
- List getJiraIssues(String label);
+ List getJiraIssues(String label) throws ConnectorException;
/**
* Synchronizes the list of {@link OrderElement}s,
@@ -82,9 +88,42 @@ public interface IJiraOrderElementSynchronizer {
*/
void syncOrderElementsWithJiraIssues(List issues, Order order);
+ /**
+ * Saves synchronization info
+ *
+ * @param key
+ * the key(label)
+ * @param order
+ * an order which already synchronized
+ */
+ void saveSyncInfo(String key, Order order);
+
+ /**
+ * Gets the most recent synchronized info
+ *
+ * @param order
+ * the order
+ * @return recent synchronized time sheet info
+ */
+ OrderSyncInfo getOrderLastSyncInfo(Order order);
+
/**
* returns synchronization info, success or fail info
*/
- JiraSyncInfo getJiraSyncInfo();
+ SynchronizationInfo getSynchronizationInfo();
+ /**
+ * Synchronize order elements with JIRA issues if they already synchronized
+ * using
+ * {@link IJiraOrderElementSynchronizer#syncOrderElementsWithJiraIssues(List, Order)
+ *
+ * It gets then an already synchronized orders from the
+ * {@link OrderSyncInfo} and re-synchronize them
+ *
+ * @return a list of {@link SynchronizationInfo}
+ *
+ * @throws ConnectorException
+ * if connector not found or contains invalid connection values
+ */
+ List syncOrderElementsWithJiraIssues() throws ConnectorException;
}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraTimesheetSynchronizer.java b/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraTimesheetSynchronizer.java
index 9c263923c..7611a75f3 100644
--- a/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraTimesheetSynchronizer.java
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/IJiraTimesheetSynchronizer.java
@@ -21,6 +21,7 @@ package org.libreplan.importers;
import java.util.List;
+import org.libreplan.business.common.entities.ConnectorException;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.workreports.entities.WorkReportType;
import org.libreplan.importers.jira.IssueDTO;
@@ -42,7 +43,6 @@ public interface IJiraTimesheetSynchronizer {
/**
* Synchronize jira timesheet with the specified jira issues .
*
- *
* Loop through all jira issues and check if timesheet is
* already exist for the specified issue item. If it is, update the
* timesheet with that issue item. If not create new one
@@ -51,12 +51,14 @@ public interface IJiraTimesheetSynchronizer {
* the jira issues
* @param order
* an existing order
+ * @throws ConnectorException
+ * if not valid connector or connector contains invalid values
*/
- void syncJiraTimesheetWithJiraIssues(List issues, Order order);
+ void syncJiraTimesheetWithJiraIssues(List issues, Order order) throws ConnectorException;
/**
* returns synchronization info, success or fail info
*/
- JiraSyncInfo getJiraSyncInfo();
+ SynchronizationInfo getSynchronizationInfo();
}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/ISchedulerManager.java b/libreplan-webapp/src/main/java/org/libreplan/importers/ISchedulerManager.java
new file mode 100644
index 000000000..745ef51a3
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/ISchedulerManager.java
@@ -0,0 +1,102 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers;
+
+import org.libreplan.business.common.entities.JobSchedulerConfiguration;
+import org.quartz.SchedulerException;
+import org.springframework.scheduling.quartz.CronTriggerBean;
+import org.springframework.scheduling.quartz.JobDetailBean;
+
+/**
+ * A manager(client) that dynamically creates jobs and cron-triggers using
+ * spring quartz library.
+ *
+ * The start and destroy of the scheduler itself is managed by the Spring
+ * framework. The scheduler starts automatically when the application starts and
+ * destroyed when the application stops.
+ *
+ * This manager (un)schedules the jobs based on the configuration
+ * {@link JobSchedulerConfiguration} entity once the scheduler starts.
+ *
+ *
+ * - Schedule job:create job {@link JobDetailBean} and cron-trigger
+ * {@link CronTriggerBean}, associated the trigger with the job and add it to
+ * the scheduler.
+ *
-
+ *
- Delete job: search the job in the scheduler and if found
+ * unschedule(delete) the job
+ *
+ *
+ * @author Miciele Ghiorghis
+ */
+public interface ISchedulerManager {
+
+ /**
+ * Reads the jobs to be scheduled from the {@link JobSchedulerConfiguration}
+ * and schedules the jobs based on the cron expression defined for each job
+ * in the {@link JobSchedulerConfiguration}
+ */
+ void scheduleJobs();
+
+ /**
+ * Reads the jobs to be scheduled from the specified
+ * {@link JobSchedulerConfiguration} and (un)schedule it
+ * accordingly
+ *
+ * In the specified {@link JobSchedulerConfiguration}
+ *
+ *
+ * {@link JobSchedulerConfiguration#getConnectorName()}
+ * check if job has a connector and the connector is activated
+ * {@link JobSchedulerConfiguration#isSchedule()} if true
+ * the job would be scheduled, if not job deleted
+ *
+ *
+ * @param jobSchedulerConfiguration
+ * configuration for job to be (un)scheduled
+ * @throws SchedulerException
+ * if unable to (un)schedule
+ */
+ void scheduleOrUnscheduleJob(JobSchedulerConfiguration jobSchedulerConfiguration) throws SchedulerException;
+
+ /**
+ * Deletes the job from the scheduler for the specified job by
+ * {@link JobSchedulerConfiguration}, if the job is already in
+ * the scheduler
+ *
+ * @param jobSchedulerConfiguration
+ * configuration for job to be deleted
+ * @throws SchedulerException
+ * if unable to delete
+ */
+ void deleteJob(JobSchedulerConfiguration jobSchedulerConfiguration) throws SchedulerException;
+
+ /**
+ * gets the next fire time for the specified job from
+ * {@link JobSchedulerConfiguration} if job is already scheduled. This is
+ * only neede for UI
+ *
+ * @param jobSchedulerConfiguration
+ * configuration to check for next fire time
+ * @return next fire time or empty string
+ */
+ String getNextFireTime(JobSchedulerConfiguration jobSchedulerConfiguration);
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTim.java b/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTim.java
new file mode 100644
index 000000000..54987566e
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTim.java
@@ -0,0 +1,470 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers;
+
+import static org.libreplan.web.I18nHelper._;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.joda.time.LocalDate;
+import org.libreplan.business.calendars.daos.ICalendarExceptionTypeDAO;
+import org.libreplan.business.calendars.entities.CalendarException;
+import org.libreplan.business.calendars.entities.CalendarExceptionType;
+import org.libreplan.business.calendars.entities.Capacity;
+import org.libreplan.business.calendars.entities.PredefinedCalendarExceptionTypes;
+import org.libreplan.business.calendars.entities.ResourceCalendar;
+import org.libreplan.business.common.IAdHocTransactionService;
+import org.libreplan.business.common.IOnTransaction;
+import org.libreplan.business.common.daos.IConnectorDAO;
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.common.entities.ConnectorException;
+import org.libreplan.business.common.entities.PredefinedConnectorProperties;
+import org.libreplan.business.common.entities.PredefinedConnectors;
+import org.libreplan.business.common.exceptions.InstanceNotFoundException;
+import org.libreplan.business.resources.daos.IWorkerDAO;
+import org.libreplan.business.resources.entities.Worker;
+import org.libreplan.business.workingday.EffortDuration;
+import org.libreplan.importers.RosterException.RosterExceptionItem;
+import org.libreplan.importers.tim.DataDTO;
+import org.libreplan.importers.tim.DepartmentDTO;
+import org.libreplan.importers.tim.FilterDTO;
+import org.libreplan.importers.tim.PeriodDTO;
+import org.libreplan.importers.tim.PersonDTO;
+import org.libreplan.importers.tim.RosterCategoryDTO;
+import org.libreplan.importers.tim.RosterDTO;
+import org.libreplan.importers.tim.RosterRequestDTO;
+import org.libreplan.importers.tim.RosterResponseDTO;
+import org.libreplan.web.calendars.IBaseCalendarModel;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Implementation of import roosters from tim
+ *
+ * @author Miciele Ghiorghis
+ */
+@Component
+@Scope(BeanDefinition.SCOPE_SINGLETON)
+public class ImportRosterFromTim implements IImportRosterFromTim {
+
+ private static final Log LOG = LogFactory.getLog(ImportRosterFromTim.class);
+
+ @Autowired
+ private IWorkerDAO workerDAO;
+
+ @Autowired
+ private IConnectorDAO connectorDAO;
+
+ @Autowired
+ private IAdHocTransactionService adHocTransactionService;
+
+ @Autowired
+ private ICalendarExceptionTypeDAO calendarExceptionTypeDAO;
+
+ @Autowired
+ @Qualifier("subclass")
+ private IBaseCalendarModel baseCalendarModel;
+
+ private SynchronizationInfo synchronizationInfo;
+
+ /**
+ * Search criteria for roster exception days in RESPONSE message
+ * {@link RosterDTO}
+ */
+ private static final String ABSENT = "Afwezig";
+
+ /**
+ * The word "Vakantie"(holiday) in RESPONSE message that would be translated
+ * to {@link PredefinedCalendarExceptionTypes#RESOURCE_HOLIDAY }
+ */
+ private static final String HOLIDAY = "Vakantie";
+
+ /**
+ * The word "Feestdag"(bank holiday) in RESPONSE message that would be
+ * translated to {@link PredefinedCalendarExceptionTypes#BANK_HOLIDAY}
+ */
+ private static final String BANK_HOLIDAY = "Feestdag";
+
+
+ @Override
+ @Transactional
+ public List importRosters() throws ConnectorException {
+ Connector connector = connectorDAO
+ .findUniqueByName(PredefinedConnectors.TIM.getName());
+ if (connector == null) {
+ throw new ConnectorException(_("Tim connector not found"));
+ }
+
+ if (!connector.areConnectionValuesValid()) {
+ throw new ConnectorException(
+ _("Connection values of Tim connector are invalid"));
+ }
+
+ Map properties = connector.getPropertiesAsMap();
+ String url = properties.get(PredefinedConnectorProperties.SERVER_URL);
+
+ String userName = properties
+ .get(PredefinedConnectorProperties.USERNAME);
+
+ String password = properties
+ .get(PredefinedConnectorProperties.PASSWORD);
+
+ int nrDaysRosterFromTim = Integer.parseInt(properties
+ .get(PredefinedConnectorProperties.TIM_NR_DAYS_ROSTER));
+
+ int productivityFactor = Integer.parseInt(properties
+ .get(PredefinedConnectorProperties.TIM_PRODUCTIVITY_FACTOR));
+
+
+ String departmentIds = properties
+ .get(PredefinedConnectorProperties.TIM_DEPARTAMENTS_IMPORT_ROSTER);
+
+ if (StringUtils.isBlank(departmentIds)) {
+ LOG.warn("No departments configured");
+ throw new ConnectorException(_("No departments configured"));
+ }
+
+ String[] departmentIdsArray = StringUtils.stripAll(StringUtils.split(
+ departmentIds, ","));
+
+ List syncInfos = new ArrayList();
+
+ for (String department : departmentIdsArray) {
+ LOG.info("Department: " + department);
+
+ synchronizationInfo = new SynchronizationInfo(_(
+ "Import roster for department {0}", department));
+
+ RosterRequestDTO rosterRequestDTO = createRosterRequest(department,
+ nrDaysRosterFromTim);
+ RosterResponseDTO rosterResponseDTO = TimSoapClient
+ .sendRequestReceiveResponse(url, userName, password,
+ rosterRequestDTO, RosterResponseDTO.class);
+
+ if (rosterResponseDTO != null) {
+ updateWorkersCalendarException(rosterResponseDTO,
+ productivityFactor);
+ if (!synchronizationInfo.isSuccessful()) {
+ syncInfos.add(synchronizationInfo);
+ }
+ } else {
+ LOG.error("No valid response for department " + department);
+ synchronizationInfo.addFailedReason(_(
+ "No valid response for department \"{0}\"",
+ department));
+ syncInfos.add(synchronizationInfo);
+ }
+ }
+ return syncInfos;
+ }
+
+ /**
+ * updates workers Exception calendar
+ *
+ * @param rosterResponse
+ * the response from Tim SOAP server
+ */
+ private void updateWorkersCalendarException(
+ final RosterResponseDTO rosterResponse, final int productivityFactor) {
+ adHocTransactionService
+ .runOnAnotherTransaction(new IOnTransaction() {
+
+ @Override
+ public Void execute() {
+ List rosterExceptions = getRosterExceptions(
+ rosterResponse, productivityFactor);
+ if (!rosterExceptions.isEmpty()) {
+ updateCalendarException(rosterExceptions);
+ } else {
+ LOG.info("No roster-exceptions found in the response");
+ synchronizationInfo
+ .addFailedReason(_("No roster-exceptions found in the response"));
+ }
+ return null;
+ }
+ });
+ }
+
+ /**
+ * Loops through rosterResponseDTO and creates
+ * {@link RosterException}s and link them to the worker
+ *
+ * @param rosterResponseDTO
+ * the response
+ * @return a list of RosterExceptions
+ */
+ private List getRosterExceptions(
+ RosterResponseDTO rosterResponseDTO, int productivityFactor) {
+ Map> map = getRosterExceptionPerWorker(rosterResponseDTO);
+
+ List rosterExceptions = new ArrayList();
+
+ for (Map.Entry> entry : map.entrySet()) {
+ Worker worker = null;
+ String workerCode = entry.getKey();
+ try {
+ worker = workerDAO.findUniqueByNif(workerCode);
+ } catch (InstanceNotFoundException e) {
+ LOG.warn("Worker '" + workerCode + "' not found");
+ synchronizationInfo.addFailedReason(_(
+ "Worker \"{0}\" not found",
+ workerCode));
+ }
+ if (worker != null) {
+ List list = entry.getValue();
+ Collections.sort(list, new Comparator() {
+ @Override
+ public int compare(RosterDTO o1, RosterDTO o2) {
+ return o1.getDate().compareTo(o2.getDate());
+ }
+ });
+ RosterException re = new RosterException(worker,
+ productivityFactor);
+ re.addRosterExceptions(list);
+ rosterExceptions.add(re);
+ }
+ }
+ return rosterExceptions;
+ }
+
+ /**
+ * Filters the roster on exceptions(absence) and creates a map with
+ * personsNetwork-name as key
+ * and list of roster-exception as value
+ *
+ * @param rosterResponseDTO
+ * the response
+ * @return person-roster exception map
+ */
+ private Map> getRosterExceptionPerWorker(
+ RosterResponseDTO rosterResponseDTO) {
+ Map> rosterMap = new HashMap>();
+ List rosterDTOs = rosterResponseDTO.getRosters();
+ for (RosterDTO rosterDTO : rosterDTOs) {
+ if (rosterDTO.getPrecence().equals(ABSENT)) {
+ String personsNetWorkName = rosterDTO.getPersons().get(0)
+ .getNetworkName();
+ if (!rosterMap.containsKey(personsNetWorkName)) {
+ rosterMap.put(personsNetWorkName,
+ new ArrayList());
+ }
+ rosterMap.get(personsNetWorkName).add(rosterDTO);
+ }
+ }
+ return rosterMap;
+ }
+
+ /**
+ * updates the workers calendar exception
+ *
+ * @param rosterExceptions
+ * list of roster exceptions
+ */
+ private void updateCalendarException(List rosterExceptions) {
+ for (RosterException rosterException : rosterExceptions) {
+ List items = rosterException
+ .getRosterExceptionItems();
+ for (RosterExceptionItem item : items) {
+ updateCalendarExceptionPerWorker(rosterException.getWorker(),
+ item.getDate(), item.getExceptionType(),
+ item.getEffortDuration());
+ }
+ }
+ }
+
+
+ /**
+ * updates the calendar exception of the specified
+ * {@link Worker} for the specified date
+ *
+ * @param worker
+ * the worker
+ * @param date
+ * the date of the exception
+ * @param exceptionName
+ * the exception name
+ * @param effortDuration
+ * the exceptions effortDurtaion
+ */
+ private void updateCalendarExceptionPerWorker(Worker worker,
+ LocalDate date, String exceptionName, EffortDuration effortDuration) {
+ CalendarExceptionType calendarExceptionType = getCalendarExceptionType(exceptionName);
+ if (calendarExceptionType == null) {
+ return;
+ }
+ ResourceCalendar resourceCalendar = (ResourceCalendar) worker
+ .getCalendarOrDefault();
+ CalendarException calendarExceptionDay = resourceCalendar
+ .getExceptionDay(date);
+ Capacity capacity = Capacity.create(effortDuration);
+ if (calendarExceptionDay != null) {
+ resourceCalendar.removeExceptionDay(calendarExceptionDay.getDate());
+ }
+ baseCalendarModel.initEdit(resourceCalendar);
+ baseCalendarModel.updateException(calendarExceptionType, date, date,
+ capacity);
+ baseCalendarModel.confirmSave();
+ }
+
+ /**
+ * Searches and returns the calendarExcptionType based on the specified
+ * name
+ *
+ * If the specified parameter name contains the word
+ * {@link ImportRosterFromTim#HOLIDAY}, the
+ * calendarExceptionType assumed to be the
+ * {@link PredefinedCalendarExceptionTypes#RESOURCE_HOLIDAY}, otherwise it
+ * searches in {@link CalendarExceptionType} for unique
+ * calendarExceptionType
+ *
+ * @param name
+ * the exception calendar name
+ */
+ private CalendarExceptionType getCalendarExceptionType(String name) {
+ if (name == null || name.isEmpty()) {
+ LOG.error("Exception name should not be empty");
+ synchronizationInfo
+ .addFailedReason(_("Exception name should not be empty"));
+ return null;
+ }
+ try {
+ String nameToSearch = name;
+ if (nameToSearch.contains(HOLIDAY)) {
+ nameToSearch = PredefinedCalendarExceptionTypes.RESOURCE_HOLIDAY
+ .toString();
+ } else if (nameToSearch.equals(BANK_HOLIDAY)) {
+ nameToSearch = PredefinedCalendarExceptionTypes.BANK_HOLIDAY
+ .toString();
+ }
+ return calendarExceptionTypeDAO.findUniqueByName(nameToSearch);
+ } catch (InstanceNotFoundException e) {
+ LOG.error("Calendar exceptionType not found", e);
+ synchronizationInfo
+ .addFailedReason(_("Calendar exception day not found"));
+ }
+ return null;
+ }
+
+
+ /**
+ * creates and returns {@link RosterRequestDTO}
+ *
+ * @param nrDaysRosterFromTim
+ * nr of days required to set the end date
+ */
+ private RosterRequestDTO createRosterRequest(String department,
+ int nrDaysRosterFromTim) {
+ RosterDTO rosterDTO = createRoster(nrDaysRosterFromTim);
+
+ PeriodDTO periodeDTO = new PeriodDTO();
+ periodeDTO.setStart(new org.joda.time.DateTime());
+ periodeDTO.setEnd(new org.joda.time.DateTime()
+ .plusDays(nrDaysRosterFromTim));
+ List periodDTOs = new ArrayList();
+ periodDTOs.add(periodeDTO);
+
+ DepartmentDTO departmentDTO = new DepartmentDTO();
+ departmentDTO.setRef(department);
+
+ FilterDTO filterDTO = new FilterDTO();
+ filterDTO.setPeriods(periodDTOs);
+ filterDTO.setDepartment(departmentDTO);
+
+ rosterDTO.setFilter(filterDTO);
+
+ rosterDTO.setPersons(createEmptyPerson());
+
+ rosterDTO.setRosterCategories(createEmptyRosterCategory());
+
+ rosterDTO.setDepartment(departmentDTO);
+
+ rosterDTO.setPrecence(new String());
+ rosterDTO.setPeriods(periodDTOs);
+
+ RosterRequestDTO exportRosterRequestDTO = new RosterRequestDTO();
+ DataDTO dataDTO = new DataDTO();
+ dataDTO.setData(rosterDTO);
+
+ exportRosterRequestDTO.setData(dataDTO);
+ return exportRosterRequestDTO;
+
+ }
+
+ /**
+ * creates and returns list of {@link PersonDTO} with empty
+ * {@link PersonDTO}
+ *
+ * This is an indication to Tim server that it should include this Person
+ * information in the RESPONSE message
+ */
+ private List createEmptyPerson() {
+ List personDTOs = new ArrayList();
+ personDTOs.add(new PersonDTO());
+ return personDTOs;
+ }
+
+ /**
+ * creates and returns list of {@link RosterCategoryDTO} with empty
+ * {@link RosterCategoryDTO}
+ *
+ * This is an indication to Tim server that it should include this
+ * RosterCategory information in the RESPONSE message
+ */
+ private List createEmptyRosterCategory() {
+ List rosterCategorieDTOs = new ArrayList();
+ RosterCategoryDTO rosterCategoryDTO = new RosterCategoryDTO();
+ rosterCategoryDTO.setName(new String());
+ rosterCategorieDTOs.add(rosterCategoryDTO);
+ return rosterCategorieDTOs;
+ }
+
+ /**
+ * creates and returns {@link RosterDTO}
+ */
+ private RosterDTO createRoster(int nrDaysRosterFromTim) {
+ RosterDTO rosterDTO = new RosterDTO();
+ rosterDTO.setStartDate(new LocalDate());
+ rosterDTO.setEndDate(new LocalDate().plusDays(nrDaysRosterFromTim));
+ rosterDTO.setResourcePlanning(false);
+ rosterDTO.setDayPlanning(false);
+ rosterDTO.setCalendar(false);
+ rosterDTO.setNonPlaned(true);
+ rosterDTO.setFullDay(false);
+ rosterDTO.setConcept(false);
+ return rosterDTO;
+ }
+
+ @Override
+ public SynchronizationInfo getSynchronizationInfo() {
+ return synchronizationInfo;
+ }
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTimJob.java b/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTimJob.java
new file mode 100644
index 000000000..b4e928eaa
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/ImportRosterFromTimJob.java
@@ -0,0 +1,69 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers;
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.libreplan.business.common.entities.ConnectorException;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Scope;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+import org.springframework.stereotype.Component;
+
+/**
+ * A job that import rosters from Tim SOAP server
+ *
+ * @author Miciele Ghiorghis
+ */
+@Component
+@Scope(BeanDefinition.SCOPE_SINGLETON)
+public class ImportRosterFromTimJob extends QuartzJobBean {
+ private static final Log LOG = LogFactory
+ .getLog(ImportRosterFromTimJob.class);
+
+ @Override
+ protected void executeInternal(JobExecutionContext context)
+ throws JobExecutionException {
+ ApplicationContext applicationContext = (ApplicationContext) context
+ .getJobDetail().getJobDataMap().get("applicationContext");
+
+ IImportRosterFromTim importRosterFromTim = (IImportRosterFromTim) applicationContext
+ .getBean("importRosterFromTim");
+
+ try {
+ List syncInfos = importRosterFromTim
+ .importRosters();
+
+ LOG.info("Import scuccessful: "
+ + (syncInfos == null || syncInfos.isEmpty()));
+
+ } catch (ConnectorException e) {
+ LOG.error("Import roster from Tim failed", e);
+ }
+
+ }
+
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraOrderElementSynchronizer.java b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraOrderElementSynchronizer.java
index b723ae2c6..354188e24 100755
--- a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraOrderElementSynchronizer.java
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraOrderElementSynchronizer.java
@@ -19,6 +19,8 @@
package org.libreplan.importers;
+import static org.libreplan.web.I18nHelper._;
+
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.MalformedURLException;
@@ -28,8 +30,11 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Map;
import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.joda.time.LocalDate;
import org.libreplan.business.advance.bootstrap.PredefinedAdvancedTypes;
import org.libreplan.business.advance.entities.AdvanceMeasurement;
@@ -37,18 +42,26 @@ import org.libreplan.business.advance.entities.AdvanceType;
import org.libreplan.business.advance.entities.DirectAdvanceAssignment;
import org.libreplan.business.advance.exceptions.DuplicateAdvanceAssignmentForOrderElementException;
import org.libreplan.business.advance.exceptions.DuplicateValueTrueReportGlobalAdvanceException;
-import org.libreplan.business.common.daos.IConfigurationDAO;
-import org.libreplan.business.common.entities.JiraConfiguration;
+import org.libreplan.business.common.IAdHocTransactionService;
+import org.libreplan.business.common.IOnTransaction;
+import org.libreplan.business.common.daos.IConnectorDAO;
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.common.entities.ConnectorException;
+import org.libreplan.business.common.entities.PredefinedConnectorProperties;
+import org.libreplan.business.common.entities.PredefinedConnectors;
+import org.libreplan.business.orders.daos.IOrderSyncInfoDAO;
import org.libreplan.business.orders.entities.HoursGroup;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.orders.entities.OrderElement;
import org.libreplan.business.orders.entities.OrderLine;
+import org.libreplan.business.orders.entities.OrderSyncInfo;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.importers.jira.IssueDTO;
import org.libreplan.importers.jira.StatusDTO;
import org.libreplan.importers.jira.TimeTrackingDTO;
import org.libreplan.importers.jira.WorkLogDTO;
import org.libreplan.importers.jira.WorkLogItemDTO;
+import org.libreplan.web.orders.IOrderModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Scope;
@@ -64,17 +77,36 @@ import org.springframework.transaction.annotation.Transactional;
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchronizer {
+ private static final Log LOG = LogFactory
+ .getLog(JiraOrderElementSynchronizer.class);
+
+ private SynchronizationInfo synchronizationInfo;
+
@Autowired
- private IConfigurationDAO configurationDAO;
+ private IConnectorDAO connectorDAO;
- private JiraSyncInfo jiraSyncInfo;
+ @Autowired
+ private IOrderSyncInfoDAO orderSyncInfoDAO;
+ @Autowired
+ private IAdHocTransactionService adHocTransactionService;
+
+ @Autowired
+ private IOrderModel orderModel;
+
+ @Autowired
+ private IJiraTimesheetSynchronizer jiraTimesheetSynchronizer;
@Override
@Transactional(readOnly = true)
- public List getAllJiraLabels() {
- String jiraLabels = configurationDAO.getConfiguration()
- .getJiraConfiguration().getJiraLabels();
+ public List getAllJiraLabels() throws ConnectorException {
+ Connector connector = getJiraConnector();
+ if (connector == null) {
+ throw new ConnectorException(_("JIRA connector not found"));
+ }
+
+ String jiraLabels = connector.getPropertiesAsMap().get(
+ PredefinedConnectorProperties.JIRA_LABELS);
String labels;
try {
@@ -88,13 +120,39 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz
@Override
@Transactional(readOnly = true)
- public List getJiraIssues(String label) {
- JiraConfiguration jiraConfiguration = configurationDAO
- .getConfiguration().getJiraConfiguration();
+ public List getJiraIssues(String label) throws ConnectorException {
- String url = jiraConfiguration.getJiraUrl();
- String username = jiraConfiguration.getJiraUserId();
- String password = jiraConfiguration.getJiraPassword();
+ Connector connector = getJiraConnector();
+ if (connector == null) {
+ throw new ConnectorException(_("JIRA connector not found"));
+ }
+
+ if (!connector.areConnectionValuesValid()) {
+ throw new ConnectorException(
+ _("Connection values of JIRA connector are invalid"));
+ }
+
+ return getJiraIssues(label, connector);
+ }
+
+ /**
+ * Gets all jira issues for the specified label
+ *
+ * @param label
+ * the search criteria
+ * @param connector
+ * where to read the configuration parameters
+ * @return a list of {@link IssueDTO}
+ */
+ private List getJiraIssues(String label, Connector connector) {
+ Map properties = connector.getPropertiesAsMap();
+ String url = properties.get(PredefinedConnectorProperties.SERVER_URL);
+
+ String username = properties
+ .get(PredefinedConnectorProperties.USERNAME);
+
+ String password = properties
+ .get(PredefinedConnectorProperties.PASSWORD);
String path = JiraRESTClient.PATH_SEARCH;
String query = "labels=" + label;
@@ -109,17 +167,20 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz
@Transactional(readOnly = true)
public void syncOrderElementsWithJiraIssues(List issues, Order order) {
- jiraSyncInfo = new JiraSyncInfo();
+ synchronizationInfo = new SynchronizationInfo(_(
+ "Synchronization order {0}", order.getName()));
for (IssueDTO issue : issues) {
- String code = JiraConfiguration.CODE_PREFIX + order.getCode() + "-"
+ String code = PredefinedConnectorProperties.JIRA_CODE_PREFIX
+ + order.getCode() + "-"
+ issue.getKey();
String name = issue.getFields().getSummary();
OrderLine orderLine = syncOrderLine(order, code, name);
if (orderLine == null) {
- jiraSyncInfo.addSyncFailedReason("Order-element for '"
- + issue.getKey() + "' issue not found");
+ synchronizationInfo.addFailedReason(_(
+ "Order-element for \"{0}\" issue not found",
+ issue.getKey()));
continue;
}
@@ -129,8 +190,9 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz
.getTimetracking(), loggedHours);
if (estimatedHours.isZero()) {
- jiraSyncInfo.addSyncFailedReason("Estimated time for '"
- + issue.getKey() + "' issue is 0");
+ synchronizationInfo.addFailedReason(_(
+ "Estimated time for \"{0}\" issue is 0",
+ issue.getKey()));
continue;
}
@@ -217,15 +279,16 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz
WorkLogDTO workLog = issue.getFields().getWorklog();
if (workLog == null) {
- jiraSyncInfo.addSyncFailedReason("No worklogs found for '"
- + issue.getKey() + "' issue");
+ synchronizationInfo.addFailedReason(_(
+ "No worklogs found for \"{0}\" issue", issue.getKey()));
return;
}
List workLogItems = workLog.getWorklogs();
if (workLogItems.isEmpty()) {
- jiraSyncInfo.addSyncFailedReason("No worklog items found for '"
- + issue.getKey() + "' issue");
+ synchronizationInfo.addFailedReason(_(
+ "No worklog items found for \"{0}\" issue",
+ issue.getKey()));
return;
}
@@ -334,9 +397,10 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz
} catch (DuplicateAdvanceAssignmentForOrderElementException e) {
// This could happen if a parent or child of the current
// OrderElement has an advance of type PERCENTAGE
- jiraSyncInfo
- .addSyncFailedReason("Duplicate value AdvanceAssignment for order element of '"
- + orderElement.getCode() + "'");
+ synchronizationInfo
+ .addFailedReason(_(
+ "Duplicate value AdvanceAssignment for order element of \"{0}\"",
+ orderElement.getCode()));
return;
}
}
@@ -393,8 +457,109 @@ public class JiraOrderElementSynchronizer implements IJiraOrderElementSynchroniz
}
@Override
- public JiraSyncInfo getJiraSyncInfo() {
- return jiraSyncInfo;
+ public SynchronizationInfo getSynchronizationInfo() {
+ return synchronizationInfo;
}
+ /**
+ * returns JIRA connector
+ */
+ private Connector getJiraConnector() {
+ return connectorDAO.findUniqueByName(PredefinedConnectors.JIRA
+ .getName());
+ }
+
+ @Override
+ @Transactional
+ public void saveSyncInfo(final String key, final Order order) {
+ adHocTransactionService
+ .runOnAnotherTransaction(new IOnTransaction() {
+ @Override
+ public Void execute() {
+ OrderSyncInfo orderSyncInfo = orderSyncInfoDAO
+ .findByKeyOrderAndConnectorName(key, order,
+ PredefinedConnectors.JIRA.getName());
+ if (orderSyncInfo == null) {
+ orderSyncInfo = OrderSyncInfo.create(key, order,
+ PredefinedConnectors.JIRA.getName());
+ }
+ orderSyncInfo.setLastSyncDate(new Date());
+ orderSyncInfoDAO.save(orderSyncInfo);
+ return null;
+ }
+ });
+ }
+
+
+ @Override
+ @Transactional(readOnly = true)
+ public OrderSyncInfo getOrderLastSyncInfo(Order order) {
+ return orderSyncInfoDAO.findLastSynchronizedInfoByOrderAndConnectorName(
+ order, PredefinedConnectors.JIRA.getName());
+
+ }
+
+ @Override
+ @Transactional
+ public List syncOrderElementsWithJiraIssues() throws ConnectorException {
+ Connector connector = getJiraConnector();
+ if (connector == null) {
+ throw new ConnectorException(_("JIRA connector not found"));
+ }
+ if (!connector.areConnectionValuesValid()) {
+ throw new ConnectorException(
+ _("Connection values of JIRA connector are invalid"));
+ }
+
+ List orderSyncInfos = orderSyncInfoDAO
+ .findByConnectorName(PredefinedConnectors.JIRA.getName());
+
+ synchronizationInfo = new SynchronizationInfo(_("Synchronization"));
+
+ List syncInfos = new ArrayList();
+
+ if (orderSyncInfos == null || orderSyncInfos.isEmpty()) {
+ LOG.warn("No items found in 'OrderSyncInfo' to synchronize with JIRA issues");
+ synchronizationInfo
+ .addFailedReason(_("No items found in 'OrderSyncInfo' to synchronize with JIRA issues"));
+ syncInfos.add(synchronizationInfo);
+ return syncInfos;
+ }
+
+ for (OrderSyncInfo orderSyncInfo : orderSyncInfos) {
+ Order order = orderSyncInfo.getOrder();
+ LOG.info("Synchronizing '" + order.getName() + "'");
+ synchronizationInfo = new SynchronizationInfo(_(
+ "Synchronization order {0}", order.getName()));
+
+ List issueDTOs = getJiraIssues(orderSyncInfo.getKey(),
+ connector);
+ if (issueDTOs == null || issueDTOs.isEmpty()) {
+ LOG.warn("No JIRA issues found for '" + orderSyncInfo.getKey()
+ + "'");
+ synchronizationInfo.addFailedReason(_(
+ "No JIRA issues found for key {0}",
+ orderSyncInfo.getKey()));
+ syncInfos.add(synchronizationInfo);
+ continue;
+ }
+
+ orderModel.initEdit(order, null);
+ syncOrderElementsWithJiraIssues(issueDTOs, order);
+ if (!synchronizationInfo.isSuccessful()) {
+ syncInfos.add(synchronizationInfo);
+ continue;
+ }
+ orderModel.save(false);
+
+ saveSyncInfo(orderSyncInfo.getKey(), order);
+
+ jiraTimesheetSynchronizer.syncJiraTimesheetWithJiraIssues(
+ issueDTOs, order);
+ if (!synchronizationInfo.isSuccessful()) {
+ syncInfos.add(synchronizationInfo);
+ }
+ }
+ return syncInfos;
+ }
}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraOrderElementSynchronizerJob.java b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraOrderElementSynchronizerJob.java
new file mode 100644
index 000000000..0b2a43520
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraOrderElementSynchronizerJob.java
@@ -0,0 +1,63 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers;
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.libreplan.business.common.entities.ConnectorException;
+import org.quartz.JobExecutionContext;
+import org.quartz.JobExecutionException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+
+/**
+ * A job that synchronizes order elements and time sheets with JIRA issues
+ *
+ * @author Miciele Ghiorghis
+ */
+public class JiraOrderElementSynchronizerJob extends QuartzJobBean {
+
+ private static final Log LOG = LogFactory
+ .getLog(JiraOrderElementSynchronizerJob.class);
+
+ @Override
+ protected void executeInternal(JobExecutionContext context)
+ throws JobExecutionException {
+ ApplicationContext applicationContext = (ApplicationContext) context
+ .getJobDetail().getJobDataMap().get("applicationContext");
+
+ IJiraOrderElementSynchronizer jiraOrderElementSynchronizer = (IJiraOrderElementSynchronizer) applicationContext
+ .getBean("jiraOrderElementSynchronizer");
+
+ try {
+ List syncInfos = jiraOrderElementSynchronizer
+ .syncOrderElementsWithJiraIssues();
+
+ LOG.info("Synchronization scuccessful: "
+ + (syncInfos == null || syncInfos.isEmpty()));
+
+ } catch (ConnectorException e) {
+ LOG.error("Synchronize order elements failed", e);
+ }
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraTimesheetSynchronizer.java b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraTimesheetSynchronizer.java
index 872ddb88a..fb3e68d6c 100644
--- a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraTimesheetSynchronizer.java
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/JiraTimesheetSynchronizer.java
@@ -19,24 +19,31 @@
package org.libreplan.importers;
+import static org.libreplan.web.I18nHelper._;
+
import java.util.List;
import java.util.Set;
+import org.apache.commons.lang.StringUtils;
import org.hibernate.NonUniqueResultException;
import org.libreplan.business.common.IAdHocTransactionService;
-import org.libreplan.business.common.daos.IConfigurationDAO;
-import org.libreplan.business.common.entities.JiraConfiguration;
+import org.libreplan.business.common.daos.IConnectorDAO;
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.common.entities.ConnectorException;
+import org.libreplan.business.common.entities.PredefinedConnectorProperties;
+import org.libreplan.business.common.entities.PredefinedConnectors;
import org.libreplan.business.common.exceptions.InstanceNotFoundException;
import org.libreplan.business.costcategories.daos.ITypeOfWorkHoursDAO;
import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
+import org.libreplan.business.orders.daos.IOrderSyncInfoDAO;
import org.libreplan.business.orders.entities.Order;
import org.libreplan.business.orders.entities.OrderElement;
+import org.libreplan.business.orders.entities.OrderSyncInfo;
import org.libreplan.business.resources.daos.IWorkerDAO;
import org.libreplan.business.resources.entities.Resource;
import org.libreplan.business.resources.entities.Worker;
import org.libreplan.business.workingday.EffortDuration;
import org.libreplan.business.workreports.daos.IWorkReportDAO;
-import org.libreplan.business.workreports.daos.IWorkReportLineDAO;
import org.libreplan.business.workreports.daos.IWorkReportTypeDAO;
import org.libreplan.business.workreports.entities.PredefinedWorkReportTypes;
import org.libreplan.business.workreports.entities.WorkReport;
@@ -63,7 +70,7 @@ import org.springframework.transaction.annotation.Transactional;
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer {
- private JiraSyncInfo jiraSyncInfo;
+ private SynchronizationInfo synchronizationInfo;
private List workers;
@@ -80,9 +87,6 @@ public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer {
@Autowired
private IWorkReportDAO workReportDAO;
- @Autowired
- private IWorkReportLineDAO workReportLineDAO;
-
@Autowired
private IWorkReportModel workReportModel;
@@ -90,50 +94,69 @@ public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer {
private ITypeOfWorkHoursDAO typeOfWorkHoursDAO;
@Autowired
- private IConfigurationDAO configurationDAO;
+ private IConnectorDAO connectorDAO;
+
+ @Autowired
+ private IOrderSyncInfoDAO orderSyncInfoDAO;
@Autowired
private IAdHocTransactionService adHocTransactionService;
@Override
@Transactional
- public void syncJiraTimesheetWithJiraIssues(List issues, Order order) {
- jiraSyncInfo = new JiraSyncInfo();
+ public void syncJiraTimesheetWithJiraIssues(List issues, Order order) throws ConnectorException {
+ synchronizationInfo = new SynchronizationInfo(_("Synchronization"));
workReportType = getJiraTimesheetsWorkReportType();
typeOfWorkHours = getTypeOfWorkHours();
workers = getWorkers();
if (workers == null && workers.isEmpty()) {
- jiraSyncInfo.addSyncFailedReason("No workers found");
+ synchronizationInfo.addFailedReason(_("No workers found"));
return;
}
- String code = order.getCode() + "-" + order.getImportedLabel();
+ OrderSyncInfo orderSyncInfo = orderSyncInfoDAO
+ .findLastSynchronizedInfoByOrderAndConnectorName(order,
+ PredefinedConnectors.JIRA.getName());
+ if (orderSyncInfo == null) {
+ synchronizationInfo.addFailedReason(_(
+ "Order \"{0}\" not found. Order probalbly not synchronized",
+ order.getName()));
+ return;
+ }
+ if (StringUtils.isBlank(orderSyncInfo.getKey())) {
+ synchronizationInfo.addFailedReason(_(
+ "Key for Order \"{0}\" is empty",
+ order.getName()));
+ return;
+ }
+
+ String code = order.getCode() + "-" + orderSyncInfo.getKey();
WorkReport workReport = updateOrCreateWorkReport(code);
for (IssueDTO issue : issues) {
WorkLogDTO worklog = issue.getFields().getWorklog();
if (worklog == null) {
- jiraSyncInfo.addSyncFailedReason("No worklogs found for '"
- + issue.getKey() + "'");
+ synchronizationInfo.addFailedReason(_(
+ "No worklogs found for \"{0}\" key", issue.getKey()));
} else {
List workLogItems = worklog.getWorklogs();
if (workLogItems == null || workLogItems.isEmpty()) {
- jiraSyncInfo
- .addSyncFailedReason("No worklog items found for '"
- + issue.getKey() + "' issue");
+ synchronizationInfo.addFailedReason(_(
+ "No worklog items found for \"{0}\" issue",
+ issue.getKey()));
} else {
- String codeOrderElement = JiraConfiguration.CODE_PREFIX
+ String codeOrderElement = PredefinedConnectorProperties.JIRA_CODE_PREFIX
+ order.getCode() + "-" + issue.getKey();
OrderElement orderElement = order.getOrderElement(codeOrderElement);
if (orderElement == null) {
- jiraSyncInfo.addSyncFailedReason("Order element("
- + code + ") not found");
+ synchronizationInfo.addFailedReason(_(
+ "Order element \"{0}\" not found", code));
} else {
updateOrCreateWorkReportLineAndAddToWorkReport(workReport, orderElement,
workLogItems);
@@ -291,10 +314,31 @@ public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer {
* Returns {@link TypeOfWorkHours} configured for JIRA connector
*
* @return TypeOfWorkHours for JIRA connector
+ * @throws ConnectorException
*/
- private TypeOfWorkHours getTypeOfWorkHours() {
- return configurationDAO.getConfiguration().getJiraConfiguration()
- .getJiraConnectorTypeOfWorkHours();
+ private TypeOfWorkHours getTypeOfWorkHours() throws ConnectorException {
+ Connector connector = connectorDAO
+ .findUniqueByName(PredefinedConnectors.JIRA.getName());
+ if (connector == null) {
+ throw new ConnectorException(_("JIRA connector not found"));
+ }
+
+ TypeOfWorkHours typeOfWorkHours;
+ String name = connector.getPropertiesAsMap().get(
+ PredefinedConnectorProperties.JIRA_HOURS_TYPE);
+
+ if (StringUtils.isBlank(name)) {
+ throw new ConnectorException(
+ _("Hours type should not be empty to synchronine timesheets"));
+ }
+
+ try {
+ typeOfWorkHours = typeOfWorkHoursDAO.findUniqueByName(name);
+ } catch (InstanceNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ return typeOfWorkHours;
}
/**
@@ -336,13 +380,13 @@ public class JiraTimesheetSynchronizer implements IJiraTimesheetSynchronizer {
return worker;
}
}
- jiraSyncInfo.addSyncFailedReason("Worker('" + nif + "') not found");
+ synchronizationInfo.addFailedReason(_("Worker \"{0}\" not found", nif));
return null;
}
@Override
- public JiraSyncInfo getJiraSyncInfo() {
- return jiraSyncInfo;
+ public SynchronizationInfo getSynchronizationInfo() {
+ return synchronizationInfo;
}
}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/RosterException.java b/libreplan-webapp/src/main/java/org/libreplan/importers/RosterException.java
new file mode 100644
index 000000000..74e3630f7
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/RosterException.java
@@ -0,0 +1,188 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.joda.time.LocalDate;
+import org.libreplan.business.resources.entities.Worker;
+import org.libreplan.business.workingday.EffortDuration;
+import org.libreplan.business.workingday.IntraDayDate.PartialDay;
+import org.libreplan.importers.tim.RosterDTO;
+
+/**
+ * Class to convert the Roster response DTO to the RosterException
+ *
+ * @author Miciele Ghiorghis
+ */
+public class RosterException {
+ private Worker worker;
+ private int productivityFactor;
+
+ List rosterExceptionItems = new ArrayList();
+
+ public RosterException(Worker worker, int productivityFactor) {
+ this.worker = worker;
+ this.productivityFactor = productivityFactor;
+ }
+
+ /**
+ * Reads the rosters and add the exceptions to
+ * rosterExceptionItems
+ *
+ * @param rosterDTOs
+ * list of rosterDTO
+ */
+ public void addRosterExceptions(List rosterDTOs) {
+ Map> mapDateRosterDTO = new TreeMap>();
+
+ for (RosterDTO rosterDTO : rosterDTOs) {
+ if (!mapDateRosterDTO.containsKey(rosterDTO.getDate())) {
+ mapDateRosterDTO.put(rosterDTO.getDate(), new ArrayList());
+ }
+ mapDateRosterDTO.get(rosterDTO.getDate()).add(rosterDTO);
+
+ }
+
+ for (Map.Entry> entry : mapDateRosterDTO
+ .entrySet()) {
+ RosterExceptionItem item = new RosterExceptionItem(entry.getKey());
+ updateExceptionTypeAndEffort(item, entry.getValue());
+ rosterExceptionItems.add(item);
+ }
+ }
+
+ /**
+ * updates the exceptionType and effortDuration
+ *
+ * In Tim you can divide your exception day in different
+ * exceptionType, for example on Monday:
+ *
+ * - 4 hours RESOURCE_HOLIDAY
+ * - 2 hours STRIKE
+ * - 2 hours BANK_HOLIDAY
+ *
+ *
+ * But Libreplan allows only one exceptionType per day.
+ *
+ * In order to store different exceptionTypes per day as one
+ * exceptionType, this method gets the
+ * exceptionType from the rosterDTO with the
+ * highest duration, in this example RESOURCE_HOLIDAY as a valid exception
+ * type, but the total duration is the sum of all these exception types
+ *
+ * Again in Tim this total duration means that the worker is on holiday for
+ * that total duration, but Libreplan does the opposite. In Libreplan, the
+ * total duration in this case means that the worker is not on holiday. If
+ * he is then the total duration should be Zero. And this method does this
+ * translation
+ *
+ * @param rosterExceptionItem
+ * the rosterException item
+ * @param rosterDTOs
+ * list of rosterDTO
+ */
+ private void updateExceptionTypeAndEffort(
+ RosterExceptionItem rosterExceptionItem, List rosterDTOs) {
+ EffortDuration max = EffortDuration.zero();
+ EffortDuration sum = EffortDuration.zero();
+ String rosterCatName = rosterDTOs.get(0).getRosterCategories().get(0)
+ .getName();
+ for (RosterDTO rosterDTO : rosterDTOs) {
+ EffortDuration duration = EffortDuration
+ .parseFromFormattedString(rosterDTO.getDuration());
+ if (duration.compareTo(max) > 0) {
+ rosterCatName = rosterDTO.getRosterCategories().get(0)
+ .getName();
+ }
+ max = EffortDuration.max(max, duration);
+ sum = EffortDuration.sum(sum, duration);
+
+ }
+ EffortDuration exceptionTime = EffortDuration.zero();
+
+ EffortDuration workableTime = worker.getCalendar().getCapacityOn(
+ PartialDay.wholeDay(rosterExceptionItem.getDate()));
+
+ // Convert the total duration from Tim to the productivity time as is
+ // configured in resource calendar
+ EffortDuration productivityTime = sum.multiplyBy(productivityFactor)
+ .divideBy(100);
+
+ // Calculate the exception time
+ if (workableTime.compareTo(productivityTime) >= 0) {
+ exceptionTime = workableTime.minus(productivityTime);
+ }
+
+ rosterExceptionItem.setExceptionType(rosterCatName);
+ rosterExceptionItem.setEffortDuration(exceptionTime);
+ }
+
+ /**
+ * returns {@link Worker}
+ */
+ public Worker getWorker() {
+ return worker;
+ }
+
+ /**
+ * returns list of {@link RosterExceptionItem}
+ */
+ public List getRosterExceptionItems() {
+ return rosterExceptionItems;
+ }
+
+ /**
+ * class representing RosterExceptionItem
+ */
+ public class RosterExceptionItem {
+ private LocalDate date;
+ private String exceptionType;
+ private EffortDuration effortDuration;
+
+ public RosterExceptionItem(LocalDate date) {
+ this.date = date;
+ }
+
+ public LocalDate getDate() {
+ return date;
+ }
+
+ public String getExceptionType() {
+ return exceptionType;
+ }
+
+ public void setExceptionType(String exceptionType) {
+ this.exceptionType = exceptionType;
+ }
+
+ public EffortDuration getEffortDuration() {
+ return effortDuration;
+ }
+
+ public void setEffortDuration(EffortDuration effortDuration) {
+ this.effortDuration = effortDuration;
+ }
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/SchedulerInfo.java b/libreplan-webapp/src/main/java/org/libreplan/importers/SchedulerInfo.java
new file mode 100644
index 000000000..5cd0e15f1
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/SchedulerInfo.java
@@ -0,0 +1,60 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers;
+
+import org.libreplan.business.common.entities.JobSchedulerConfiguration;
+
+/**
+ * Holds information about the scheduler, The information comes partly form
+ * {@link JobSchedulerConfiguration} and partly form {@link SchedulerManager}
+ *
+ * @author Miciele Ghiorghis
+ */
+public class SchedulerInfo {
+
+ private JobSchedulerConfiguration jobSchedulerConfiguration;
+ private String nextFireTime;
+
+ public SchedulerInfo() {
+
+ }
+
+ public SchedulerInfo(JobSchedulerConfiguration jobSchedulerConfiguration) {
+ this.jobSchedulerConfiguration = jobSchedulerConfiguration;
+ }
+
+ public String getNextFireTime() {
+ return nextFireTime;
+ }
+
+ public void setNextFireTime(String nextFireTime) {
+ this.nextFireTime = nextFireTime;
+ }
+
+ public JobSchedulerConfiguration getJobSchedulerConfiguration() {
+ return jobSchedulerConfiguration;
+ }
+
+ public void setJobSchedulerConfiguration(
+ JobSchedulerConfiguration jobSchedulerConfiguration) {
+ this.jobSchedulerConfiguration = jobSchedulerConfiguration;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/SchedulerManager.java b/libreplan-webapp/src/main/java/org/libreplan/importers/SchedulerManager.java
new file mode 100644
index 000000000..ef1b49da0
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/SchedulerManager.java
@@ -0,0 +1,336 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers;
+
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.libreplan.business.common.daos.IConnectorDAO;
+import org.libreplan.business.common.daos.IJobSchedulerConfigurationDAO;
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.common.entities.JobClassNameEnum;
+import org.libreplan.business.common.entities.JobSchedulerConfiguration;
+import org.quartz.CronExpression;
+import org.quartz.CronTrigger;
+import org.quartz.JobExecutionContext;
+import org.quartz.Scheduler;
+import org.quartz.SchedulerException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Scope;
+import org.springframework.scheduling.quartz.CronTriggerBean;
+import org.springframework.scheduling.quartz.JobDetailBean;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * Implementation of scheduler manager
+ *
+ * @author Miciele Ghiorghis
+ */
+@Service
+@Scope(BeanDefinition.SCOPE_SINGLETON)
+public class SchedulerManager implements ISchedulerManager {
+
+ private static final Log LOG = LogFactory.getLog(SchedulerManager.class);
+
+ @Autowired
+ private Scheduler scheduler;
+
+ @Autowired
+ private IJobSchedulerConfigurationDAO jobSchedulerConfigurationDAO;
+
+ @Autowired
+ private ApplicationContext applicationContext;
+
+ @Autowired
+ private IConnectorDAO connectorDAO;
+
+
+ /**
+ * suffix for trigger -group and -name
+ */
+ private static final String TRIGGER_SUFFIX = "-TRIGGER";
+
+ public Scheduler getScheduler() {
+ return scheduler;
+ }
+
+ public void setScheduler(Scheduler scheduler) {
+ this.scheduler = scheduler;
+ }
+
+ @Override
+ public void scheduleJobs() {
+ List jobSchedulerConfigurations = jobSchedulerConfigurationDAO
+ .getAll();
+ for (JobSchedulerConfiguration conf : jobSchedulerConfigurations) {
+ try {
+ scheduleOrUnscheduleJob(conf);
+ } catch (SchedulerException e) {
+ LOG.error("Unable to schedule", e);
+ }
+ }
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public void scheduleOrUnscheduleJob(
+ JobSchedulerConfiguration jobSchedulerConfiguration) throws SchedulerException {
+
+ if (hasConnector(jobSchedulerConfiguration.getConnectorName())) {
+ if (isConnectorActivated(jobSchedulerConfiguration
+ .getConnectorName())) {
+ if (jobSchedulerConfiguration.isSchedule()) {
+ scheduleNewJob(jobSchedulerConfiguration);
+ return;
+ }
+ }
+ deleteJob(jobSchedulerConfiguration);
+ return;
+ }
+ if (!jobSchedulerConfiguration.isSchedule()) {
+ deleteJob(jobSchedulerConfiguration);
+ return;
+ }
+ scheduleNewJob(jobSchedulerConfiguration);
+ }
+
+ /**
+ * Check if {@link JobSchedulerConfiguration} has a connector
+ *
+ * @param connectorName
+ * the connector to check for
+ * @return true if connector is not null or empty
+ */
+ private boolean hasConnector(String connectorName) {
+ return !StringUtils.isBlank(connectorName);
+ }
+
+ /**
+ * Check if the specified {@link Connector} is activated
+ *
+ * @param connectorName
+ * the connector to check for activated
+ * @return true if activated
+ */
+ private boolean isConnectorActivated(String connectorName) {
+ Connector connector = connectorDAO.findUniqueByName(connectorName);
+ if (connector == null) {
+ return false;
+ }
+ return connector.isActivated();
+ }
+
+ @Override
+ public void deleteJob(JobSchedulerConfiguration jobSchedulerConfiguration) throws SchedulerException {
+ String triggerName = jobSchedulerConfiguration.getJobName()
+ + TRIGGER_SUFFIX;
+ String triggerGroup = jobSchedulerConfiguration.getJobGroup()
+ + TRIGGER_SUFFIX;
+
+ CronTriggerBean trigger = getTriggerBean(triggerName, triggerGroup);
+ if (trigger == null) {
+ LOG.warn("Trigger not found");
+ return;
+ }
+
+ if (isJobCurrentlyExecuting(triggerName, triggerGroup)) {
+ LOG.warn("Job is currently executing...");
+ return;
+ }
+
+ // deleteJob doesn't work using unscheduleJob
+ this.scheduler.unscheduleJob(trigger.getName(), trigger.getGroup());
+ }
+
+ /**
+ * Checks if job is currently running for the specified
+ * triggerName and triggerGroup
+ *
+ * @param triggerName
+ * the triggerName
+ * @param triggerGroup
+ * the triggerGroup
+ * @return true if job is currently running, otherwise false
+ */
+ @SuppressWarnings("unchecked")
+ private boolean isJobCurrentlyExecuting(String triggerName,
+ String triggerGroup) {
+ try {
+ List currentExecutingJobs = this.scheduler
+ .getCurrentlyExecutingJobs();
+ for (JobExecutionContext jobExecutionContext : currentExecutingJobs) {
+ String name = jobExecutionContext.getTrigger().getName();
+ String group = jobExecutionContext.getTrigger().getGroup();
+ if (triggerName.equals(name) && triggerGroup.equals(group)) {
+ return true;
+ }
+ }
+ } catch (SchedulerException e) {
+ LOG.error("Unable to get currently executing jobs", e);
+ }
+ return false;
+ }
+
+ /**
+ * Creates {@link CronTriggerBean} and {@link JobDetailBean} based on the
+ * specified {@link JobSchedulerConfiguration}. First delete
+ * job if exist and then schedule it
+ *
+ * @param jobSchedulerConfiguration
+ * where to reade jobs to be scheduled
+ * @throws SchedulerException
+ * if unable to delete and/or schedule job
+ */
+ private void scheduleNewJob(
+ JobSchedulerConfiguration jobSchedulerConfiguration) throws SchedulerException {
+ CronTriggerBean cronTriggerBean = createCronTriggerBean(jobSchedulerConfiguration);
+ if (cronTriggerBean == null) {
+ return;
+ }
+
+ JobDetailBean jobDetailBean = createJobDetailBean(jobSchedulerConfiguration);
+ if (jobDetailBean == null) {
+ return;
+ }
+ deleteJob(jobSchedulerConfiguration);
+ this.scheduler.scheduleJob(jobDetailBean, cronTriggerBean);
+ }
+
+ /**
+ * Creates {@link CronTriggerBean} from the specified
+ * {@link JobSchedulerConfiguration}
+ *
+ * @param jobSchedulerConfiguration
+ * configuration to create CronTriggerBean>
+ * @return the created CronTriggerBean or null if unable to
+ * create it
+ */
+ private CronTriggerBean createCronTriggerBean(
+ JobSchedulerConfiguration jobSchedulerConfiguration) {
+ CronTriggerBean cronTriggerBean = new CronTriggerBean();
+ cronTriggerBean.setName(jobSchedulerConfiguration.getJobName() + TRIGGER_SUFFIX);
+ cronTriggerBean.setGroup(jobSchedulerConfiguration.getJobGroup()
+ + TRIGGER_SUFFIX);
+
+ try {
+ cronTriggerBean.setCronExpression(new CronExpression(
+ jobSchedulerConfiguration.getCronExpression()));
+ cronTriggerBean.setJobName(jobSchedulerConfiguration.getJobName());
+ cronTriggerBean
+ .setJobGroup(jobSchedulerConfiguration.getJobGroup());
+ return cronTriggerBean;
+ } catch (ParseException e) {
+ LOG.error("Unable to parse cron expression", e);
+ }
+ return null;
+ }
+
+ /**
+ * Creates {@link JobDetailBean} from the specified
+ * {@link JobSchedulerConfiguration}
+ *
+ * @param jobSchedulerConfiguration
+ * configuration to create JobDetailBean>
+ * @return the created JobDetailBean or null if unable to it
+ */
+ private JobDetailBean createJobDetailBean(
+ JobSchedulerConfiguration jobSchedulerConfiguration) {
+ JobDetailBean jobDetailBean = new JobDetailBean();
+
+ Class> jobClass = getJobClass(jobSchedulerConfiguration
+ .getJobClassName());
+ if (jobClass == null) {
+ return null;
+ }
+
+ jobDetailBean.setName(jobSchedulerConfiguration.getJobName());
+ jobDetailBean.setGroup(jobSchedulerConfiguration.getJobGroup());
+ jobDetailBean.setJobClass(jobClass);
+
+ Map jobDataAsMap = new HashMap();
+ jobDataAsMap.put("applicationContext", applicationContext);
+ jobDetailBean.setJobDataAsMap(jobDataAsMap);
+ return jobDetailBean;
+ }
+
+
+ /**
+ * returns jobClass based on jobClassName parameter
+ *
+ * @param jobClassName
+ * job className
+ */
+ private Class> getJobClass(JobClassNameEnum jobClassName) {
+ try {
+ return Class.forName(jobClassName.getPackageName() + "."
+ + jobClassName.getName());
+ } catch (ClassNotFoundException e) {
+ LOG.error("Unable to get class object '" + jobClassName + "'", e);
+ }
+ return null;
+ }
+
+ @Override
+ public String getNextFireTime(
+ JobSchedulerConfiguration jobSchedulerConfiguration) {
+ try {
+ CronTrigger trigger = (CronTrigger) this.scheduler.getTrigger(
+ jobSchedulerConfiguration.getJobName() + TRIGGER_SUFFIX,
+ jobSchedulerConfiguration.getJobGroup()
+ + TRIGGER_SUFFIX);
+ if (trigger != null) {
+ return trigger.getNextFireTime().toString();
+ }
+ } catch (SchedulerException e) {
+ LOG.error("unable to get the trigger", e);
+ }
+ return "";
+ }
+
+ /**
+ * gets the {@link CronTriggerBean} for the specified
+ * triggerName and tirggerGroup
+ *
+ * @param triggerName
+ * the trigger name
+ * @param triggerGroup
+ * the trigger group
+ * @return CronTriggerBean if found, otherwise null
+ */
+ private CronTriggerBean getTriggerBean(String triggerName,
+ String triggerGroup) {
+ try {
+ return (CronTriggerBean) this.scheduler.getTrigger(triggerName,
+ triggerGroup);
+ } catch (SchedulerException e) {
+ LOG.error("Unable to get job trigger", e);
+ }
+ return null;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraSyncInfo.java b/libreplan-webapp/src/main/java/org/libreplan/importers/SynchronizationInfo.java
similarity index 51%
rename from libreplan-webapp/src/main/java/org/libreplan/importers/JiraSyncInfo.java
rename to libreplan-webapp/src/main/java/org/libreplan/importers/SynchronizationInfo.java
index fecf80d95..192710eb3 100644
--- a/libreplan-webapp/src/main/java/org/libreplan/importers/JiraSyncInfo.java
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/SynchronizationInfo.java
@@ -24,38 +24,57 @@ import java.util.Collections;
import java.util.List;
/**
- * Keeps track the synchronization info.
+ * Keeps track the success/failure of synchronization process
*
* @author Miciele Ghiorghis
*/
-public class JiraSyncInfo {
-
- private List syncFailedReasons = new ArrayList();
+public class SynchronizationInfo {
/**
- * Add the specified reason to syncFailedReasons list
- *
- * @param reason
- * reason why synchronizition failed
+ * The action, a unique key for example synchronization, import or export
+ * etc action
*/
- public void addSyncFailedReason(String reason) {
- syncFailedReasons.add(reason);
+ private String action;
+
+ /**
+ * Holds failed reasons
+ */
+ private List failedReasons = new ArrayList();
+
+ public SynchronizationInfo(String action) {
+ this.action = action;
}
/**
- * Is synchronization successful
- *
- * @return
+ * Returns the action
*/
- public boolean isSyncSuccessful() {
- return syncFailedReasons.isEmpty();
+ public String getAction() {
+ return action;
+ }
+
+ /**
+ * Adds the specified reason to failedReasons list
+ *
+ * @param reason
+ * reason why synchronization is failed
+ */
+ public void addFailedReason(String reason) {
+ failedReasons.add(reason);
+ }
+
+ /**
+ * Is synchronization succeeded
+ *
+ * @return true if failedReasons is empty
+ */
+ public boolean isSuccessful() {
+ return failedReasons.isEmpty();
}
/**
* returns reasons why synchronization is failed
*/
- public List getSyncFailedReasons() {
- return Collections.unmodifiableList(syncFailedReasons);
+ public List getFailedReasons() {
+ return Collections.unmodifiableList(failedReasons);
}
-
}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/TimSoapClient.java b/libreplan-webapp/src/main/java/org/libreplan/importers/TimSoapClient.java
new file mode 100644
index 000000000..edd96c064
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/TimSoapClient.java
@@ -0,0 +1,347 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers;
+
+import java.io.File;
+
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.soap.MessageFactory;
+import javax.xml.soap.Node;
+import javax.xml.soap.SOAPBody;
+import javax.xml.soap.SOAPConnection;
+import javax.xml.soap.SOAPConnectionFactory;
+import javax.xml.soap.SOAPEnvelope;
+import javax.xml.soap.SOAPException;
+import javax.xml.soap.SOAPMessage;
+import javax.xml.soap.SOAPPart;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.cxf.common.util.Base64Utility;
+import org.libreplan.importers.tim.RosterResponseDTO;
+
+/**
+ * Client to interact with Tim SOAP server.
+ *
+ * This client creates SOAP message, makes connection to the SOAP server and
+ * sends the request. It is also the task of this client to convert the
+ * response(xml document) to java objects
+ *
+ * @author Miciele Ghiorghis
+ */
+public class TimSoapClient {
+
+ private static final Log LOG = LogFactory.getLog(TimSoapClient.class);
+
+ /**
+ * Creates request message to be send to the SOAP server
+ *
+ * @param clazz
+ * object to be marshaled
+ * @param userName
+ * the user name
+ * @param password
+ * the password
+ * @return the created soap message
+ * @throws SOAPException
+ * if unable to create message or envelope
+ * @throws JAXBException
+ * if unable to marshal the clazz
+ */
+ private static SOAPMessage createRequest(T clazz, String userName,
+ String password) throws SOAPException, JAXBException {
+
+ SOAPMessage message = createMessage();
+
+ addAuthorization(message, userName, password);
+
+ SOAPEnvelope soapEnvelope = createEnvelope(message.getSOAPPart());
+
+ SOAPBody soapBody = soapEnvelope.getBody();
+ marshal(clazz, soapBody);
+
+ message.saveChanges();
+ return message;
+ }
+
+ /**
+ * Creates SOAP message to be send to the SOAP server
+ *
+ * @return the created SOAP message
+ * @throws SOAPException
+ * if unable to create soap message
+ */
+ private static SOAPMessage createMessage() throws SOAPException {
+ MessageFactory messageFactory = MessageFactory.newInstance();
+ SOAPMessage message = messageFactory.createMessage();
+ return message;
+ }
+
+ /**
+ * Adds authorization to the specified parameter message
+ *
+ * @param message
+ * the message
+ * @param username
+ * the user name
+ * @param password
+ * the password
+ */
+ private static void addAuthorization(SOAPMessage message, String username,
+ String password) {
+ String encodeUserInfo = username + ":" + password;
+ encodeUserInfo = Base64Utility.encode(encodeUserInfo.getBytes());
+ message.getMimeHeaders().setHeader("Authorization",
+ "Basic " + encodeUserInfo);
+ }
+
+ /**
+ * Creates SOAP envelope and adds namespace declaration and sets encoding
+ * style
+ *
+ * @param soapPart
+ * the message part
+ * @return the SOAP envelope
+ * @throws SOAPException
+ */
+ private static SOAPEnvelope createEnvelope(SOAPPart soapPart)
+ throws SOAPException {
+ SOAPEnvelope soapEnvelope = soapPart.getEnvelope();
+ addNamespaceDeclaration(soapEnvelope);
+ setEncodingStyle(soapEnvelope);
+ return soapEnvelope;
+ }
+
+ /**
+ * Adds namespace declaration to the specified parameter
+ * soapEnvelop
+ *
+ * @param soapEnvelope
+ * the SOAP envelope
+ * @throws SOAPException
+ */
+ private static void addNamespaceDeclaration(SOAPEnvelope soapEnvelope)
+ throws SOAPException {
+ soapEnvelope.addNamespaceDeclaration("xsd",
+ "http://www.w3.org/2001/XMLSchema");
+ soapEnvelope.addNamespaceDeclaration("xsi",
+ "http://www.w3.org/2001/XMLSchema-instance");
+ soapEnvelope.addNamespaceDeclaration("enc",
+ "http://schemas.xmlsoap.org/soap/encoding/");
+ soapEnvelope.addNamespaceDeclaration("env",
+ "http://schemas.xmlsoap.org/soap/envelop/");
+
+ }
+
+ /**
+ * Sets the encoding style to the specified parameter
+ * soapEnvelop
+ *
+ * @param soapEnvelope
+ * the SOAP envelope
+ * @throws SOAPException
+ */
+ private static void setEncodingStyle(SOAPEnvelope soapEnvelope)
+ throws SOAPException {
+ soapEnvelope
+ .setEncodingStyle("http://schemas.xmlsoap.org/soap/encoding/");
+ }
+
+ /**
+ * Marshals the specified parameter clazz to the specified
+ * soapBody
+ *
+ * @param clazz
+ * the object to be marshaled
+ * @param soapBody
+ * the SOAP body, result of marshal
+ * @throws JAXBException
+ * if marshaling failed
+ */
+ private static void marshal(T clazz, SOAPBody soapBody)
+ throws JAXBException {
+ JAXBContext jaxbContext = JAXBContext.newInstance(clazz.getClass());
+ Marshaller marshaller = jaxbContext.createMarshaller();
+ marshaller.marshal(clazz, soapBody);
+ }
+
+ /**
+ * Unmarshals the specified paramter soapBody to the specified
+ * clazz
+ *
+ * @param clazz
+ * object to hold unmarashal result
+ * @param soapBody
+ * the soap body to be unmarshalled
+ * @return the unmarashalled object
+ * @throws JAXBException
+ * if unmarshal failed
+ */
+ @SuppressWarnings("unchecked")
+ private static T unmarshal(Class clazz, SOAPBody soapBody)
+ throws JAXBException {
+ JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
+
+ Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+ Node bindElement = (Node) soapBody.getFirstChild();
+ while (bindElement.getNodeType() != Node.ELEMENT_NODE) {
+ bindElement = (Node) bindElement.getNextSibling();
+ }
+ return unmarshaller.unmarshal(bindElement, clazz).getValue();
+ }
+
+ /**
+ * Sends the SOAP message request to the SOAP server
+ *
+ * @param url
+ * the endpoint of the web service
+ * @param message
+ * the SOAP message to be send
+ * @return the response, SOAP message
+ * @throws SOAPException
+ * if unable to send request
+ */
+ private static SOAPMessage sendRequest(String url, SOAPMessage message)
+ throws SOAPException {
+ SOAPConnection connection = null;
+ SOAPMessage response = null;
+ try {
+ connection = createConnection();
+ response = connection.call(message, url);
+ } finally {
+ if (connection != null) {
+ closeConnection(connection);
+ }
+ }
+ return response;
+ }
+
+
+ /**
+ * Creates a SOAP connection to the SOAP server
+ *
+ * @return the SOAPconnection object
+ * @throws SOAPException
+ * if unable to create connection
+ */
+ private static SOAPConnection createConnection() throws SOAPException {
+ SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory
+ .newInstance();
+ SOAPConnection connection = soapConnectionFactory.createConnection();
+ return connection;
+ }
+
+ /**
+ * Closes the SOAP connection
+ *
+ * @param connection
+ * the SOAP connection
+ * @throws SOAPException
+ * if unable to close connection
+ */
+ private static void closeConnection(SOAPConnection connection)
+ throws SOAPException {
+ connection.close();
+ }
+
+ /**
+ * Sends soap request to the SOAP server. Receives and unmarshals the
+ * response
+ *
+ * @param url
+ * the SOAP server url(endpoint)
+ * @param userName
+ * the user
+ * @param password
+ * the password
+ * @param request
+ * the request object
+ * @param response
+ * the response class
+ * @return the expected object or null
+ */
+ public static T sendRequestReceiveResponse(String url,
+ String userName, String password, U request, Class response) {
+ try {
+ SOAPMessage requestMsg = createRequest(request, userName, password);
+ SOAPMessage responseMsg = sendRequest(url, requestMsg);
+ return unmarshal(response, responseMsg.getSOAPBody());
+ } catch (SOAPException soapExp) {
+ LOG.error("SOAPException: ", soapExp);
+ } catch (JAXBException jaxbExp) {
+ LOG.error("JAXBException: ", jaxbExp);
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks authorization for the specified username and
+ * password
+ *
+ * @param url
+ * webservices url
+ * @param username
+ * the user
+ * @param password
+ * the password
+ * @return true if user is authorized otherwise false
+ */
+ public static boolean checkAuthorization(String url, String username,
+ String password) {
+ try {
+ SOAPMessage message = createMessage();
+ addAuthorization(message, username, password);
+ sendRequest(url, message);
+ return true;
+ } catch (SOAPException e) {
+ LOG.error("SOAP Exception: ", e);
+ }
+ return false;
+ }
+
+ /**
+ * simulates roster response, to be used for example by unit test
+ *
+ * unmarshals the roster xml from the specified file and
+ * returns {@link RosterResponseDTO}
+ *
+ * @param file
+ * file with xml contents
+ * @return exportRosterDTO if unmarshal succeeded otherwise null
+ */
+ public static RosterResponseDTO unmarshalRosterFromFile(File file) {
+ try {
+ JAXBContext jaxbContext = JAXBContext
+ .newInstance(RosterResponseDTO.class);
+
+ Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+ RosterResponseDTO exportResponseDTO = (RosterResponseDTO) unmarshaller
+ .unmarshal(file);
+ return exportResponseDTO;
+ } catch (JAXBException e) {
+ LOG.error("Error processing response: ", e);
+ }
+ return null;
+ }
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/DataDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/DataDTO.java
new file mode 100644
index 000000000..7cfb9e2fa
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/DataDTO.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAnyElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlSeeAlso;
+
+/**
+ * DTO representing a tim-connector Data
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement
+@XmlSeeAlso({ RosterDTO.class })
+public class DataDTO {
+
+ @XmlAnyElement
+ private T data;
+
+ public T getData() {
+ return data;
+ }
+
+ public void setData(T data) {
+ this.data = data;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/DepartmentDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/DepartmentDTO.java
new file mode 100644
index 000000000..57368e9de
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/DepartmentDTO.java
@@ -0,0 +1,50 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+/**
+ * DTO representing a tim-connector Department
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement(name = "Afdeling")
+public class DepartmentDTO {
+
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ @XmlElement
+ private String ref;
+
+ public String getRef() {
+ return ref;
+ }
+
+ public void setRef(String ref) {
+ this.ref = ref;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/DurationDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/DurationDTO.java
new file mode 100644
index 000000000..9ed34bc86
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/DurationDTO.java
@@ -0,0 +1,62 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlValue;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+/**
+ * DTO representing a tim-connector Duration
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement
+public class DurationDTO {
+
+ @XmlAttribute(name = "options", required = true)
+ private String options;
+
+
+ @XmlValue
+ @XmlJavaTypeAdapter(TimDoubleAdapter.class)
+ private Double duration;
+
+ public String getOptions() {
+ return options;
+ }
+
+ public void setOptions(String options) {
+ this.options = options;
+ }
+
+ public Double getDuration() {
+ return duration;
+ }
+
+ public void setDuration(Double duration) {
+ this.duration = duration;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/FilterDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/FilterDTO.java
new file mode 100644
index 000000000..3027c475a
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/FilterDTO.java
@@ -0,0 +1,82 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * DTO representing a tim-connector filter
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement
+public class FilterDTO {
+
+ @XmlElement(name = "persoon")
+ private PersonDTO person;
+
+ @XmlElement(name = "periode")
+ private List periods;
+
+ @XmlElement(name = "Afdeling")
+ private DepartmentDTO department;
+
+ @XmlElement(name = "roostercategorie")
+ private RosterCategoryDTO rosterCategory;
+
+ public PersonDTO getPerson() {
+ return person;
+ }
+
+ public void setPerson(PersonDTO person) {
+ this.person = person;
+ }
+
+ public List getPeriods() {
+ return periods;
+ }
+
+ public void setPeriods(List periods) {
+ this.periods = periods;
+ }
+
+ public DepartmentDTO getDepartment() {
+ return department;
+ }
+
+ public void setDepartment(DepartmentDTO department) {
+ this.department = department;
+ }
+
+ public RosterCategoryDTO getRosterCategory() {
+ return rosterCategory;
+ }
+
+ public void setRosterCategory(RosterCategoryDTO rosterCategory) {
+ this.rosterCategory = rosterCategory;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/PeriodDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/PeriodDTO.java
new file mode 100644
index 000000000..7e1b81a64
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/PeriodDTO.java
@@ -0,0 +1,59 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+import org.joda.time.DateTime;
+
+/**
+ * DTO representing a tim-connector Period
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement
+public class PeriodDTO {
+
+ @XmlElement(name = "startdate", required = true, nillable = true)
+ private DateTime start;
+
+ @XmlElement(name = "enddate", required = true, nillable = true)
+ private DateTime end;
+
+ public DateTime getStart() {
+ return start;
+ }
+
+ public void setStart(DateTime start) {
+ this.start = start;
+ }
+
+ public DateTime getEnd() {
+ return end;
+ }
+
+ public void setEnd(DateTime end) {
+ this.end = end;
+ }
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/PersonDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/PersonDTO.java
new file mode 100644
index 000000000..61c112c02
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/PersonDTO.java
@@ -0,0 +1,88 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+/**
+ * DTO representing a tim-connector Person
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement(name = "Persoon")
+@XmlType(propOrder = { "networkName", "name", "emailAddress" })
+public class PersonDTO {
+
+ @XmlAttribute(name = "options")
+ private String options;
+
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ @XmlElement(name = "naam", nillable = true)
+ private String name;
+
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ @XmlElement(name = "emaladres", nillable = true)
+ private String emailAddress;
+
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ @XmlElement(name = "netwerknaam", nillable = true)
+ private String networkName;
+
+ public String getOptions() {
+ return options;
+ }
+
+ public void setOptions(String options) {
+ this.options = options;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getEmailAddress() {
+ return emailAddress;
+ }
+
+ public void setEmailAddress(String emailAddress) {
+ this.emailAddress = emailAddress;
+ }
+
+ public String getNetworkName() {
+ return networkName;
+ }
+
+ public void setNetworkName(String networkName) {
+ this.networkName = networkName;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/ProductDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/ProductDTO.java
new file mode 100644
index 000000000..e657d249d
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/ProductDTO.java
@@ -0,0 +1,72 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+/**
+ * DTO representing a tim-connector Product
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement
+public class ProductDTO {
+
+ @XmlAttribute(required = true)
+ private String options;
+
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ @XmlElement
+ private String code;
+
+ private String name;
+
+ public String getOptions() {
+ return options;
+ }
+
+ public void setOptions(String options) {
+ this.options = options;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RegistrationDateDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RegistrationDateDTO.java
new file mode 100644
index 000000000..3cd66a8fe
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RegistrationDateDTO.java
@@ -0,0 +1,61 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlValue;
+
+import org.joda.time.LocalDate;
+
+/**
+ * DTO representing a tim-connector RegistrationDate
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement(name = "datum")
+public class RegistrationDateDTO {
+
+ @XmlAttribute(name = "options", required = true)
+ private String options;
+
+ @XmlValue
+ private LocalDate date;
+
+ public String getOptions() {
+ return options;
+ }
+
+ public void setOptions(String options) {
+ this.options = options;
+ }
+
+ public LocalDate getDate() {
+ return date;
+ }
+
+ public void setDate(LocalDate date) {
+ this.date = date;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RosterCategoryDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RosterCategoryDTO.java
new file mode 100644
index 000000000..56045fff4
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RosterCategoryDTO.java
@@ -0,0 +1,76 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+/**
+ * DTO representing a tim-connector RosterCategory
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement(name = "roostercategorie")
+@XmlType(propOrder = { "name", "presence", "status" })
+public class RosterCategoryDTO {
+
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ @XmlElement(name = "naam", required = true, nillable = true)
+ private String name;
+
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ @XmlElement(name = "aanwezigheid", required = true, nillable = true)
+ private String presence;
+
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ @XmlElement(required = true, nillable = true)
+ private String status;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getPresence() {
+ return presence;
+ }
+
+ public void setPresence(String presence) {
+ this.presence = presence;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RosterDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RosterDTO.java
new file mode 100644
index 000000000..1bd831e0b
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RosterDTO.java
@@ -0,0 +1,245 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlAttribute;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.adapters.CollapsedStringAdapter;
+import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
+import org.joda.time.LocalDate;
+import org.joda.time.LocalTime;
+
+/**
+ * DTO representing a tim-connector Roster
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement(name = "bezettingblok")
+public class RosterDTO {
+
+ @XmlAttribute(name = "startdate", required = true)
+ private LocalDate startDate;
+
+ @XmlAttribute(name = "enddate", required = true)
+ private LocalDate endDate;
+
+ @XmlAttribute(name = "resource_planning")
+ private Boolean resourcePlanning;
+
+ @XmlAttribute(name = "day_planning")
+ private Boolean dayPlanning;
+
+ @XmlAttribute
+ private Boolean calendar;
+
+ @XmlAttribute(name = "non_planned")
+ private Boolean nonPlaned;
+
+ @XmlAttribute(name = "full_day")
+ private Boolean fullDay;
+
+ @XmlAttribute
+ private Boolean concept;
+
+ @XmlElement
+ private FilterDTO filter;
+
+ @XmlElement(name = "Persoon")
+ private List persons;
+
+ @XmlElement(name = "Roostercategorie")
+ private List rosterCategories;
+
+ @XmlElement(name = "Afdeling")
+ private DepartmentDTO department;
+
+ @XmlElement(name = "Datum", required = true, nillable = true)
+ private LocalDate date;
+
+ @XmlElement(name = "Tijd", required = true, nillable = true)
+ private LocalTime time;
+
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ @XmlElement(name = "duur", required = true, nillable = true)
+ private String duration;
+
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ @XmlElement(name = "aanwezigheid")
+ private String precence;
+
+ @XmlElement(name = "periode")
+ private List periods;
+
+ @XmlJavaTypeAdapter(CollapsedStringAdapter.class)
+ @XmlElement(name = "status")
+ private String status;
+
+ public LocalDate getStartDate() {
+ return startDate;
+ }
+
+ public void setStartDate(LocalDate startDate) {
+ this.startDate = startDate;
+ }
+
+ public LocalDate getEndDate() {
+ return endDate;
+ }
+
+ public void setEndDate(LocalDate endDate) {
+ this.endDate = endDate;
+ }
+
+ public Boolean getResourcePlanning() {
+ return resourcePlanning;
+ }
+
+ public void setResourcePlanning(Boolean resourcePlanning) {
+ this.resourcePlanning = resourcePlanning;
+ }
+
+ public Boolean getDayPlanning() {
+ return dayPlanning;
+ }
+
+ public void setDayPlanning(Boolean dayPlanning) {
+ this.dayPlanning = dayPlanning;
+ }
+
+ public Boolean getCalendar() {
+ return calendar;
+ }
+
+ public void setCalendar(Boolean calendar) {
+ this.calendar = calendar;
+ }
+
+ public Boolean getNonPlaned() {
+ return nonPlaned;
+ }
+
+ public void setNonPlaned(Boolean nonPlaned) {
+ this.nonPlaned = nonPlaned;
+ }
+
+ public Boolean getFullDay() {
+ return fullDay;
+ }
+
+ public void setFullDay(Boolean fullDay) {
+ this.fullDay = fullDay;
+ }
+
+ public Boolean getConcept() {
+ return concept;
+ }
+
+ public void setConcept(Boolean concept) {
+ this.concept = concept;
+ }
+
+ public FilterDTO getFilter() {
+ return filter;
+ }
+
+ public void setFilter(FilterDTO filter) {
+ this.filter = filter;
+ }
+
+ public List getPersons() {
+ return persons;
+ }
+
+ public void setPersons(List persons) {
+ this.persons = persons;
+ }
+
+ public List getRosterCategories() {
+ return rosterCategories;
+ }
+
+ public void setRosterCategories(List rosterCategories) {
+ this.rosterCategories = rosterCategories;
+ }
+
+ public DepartmentDTO getDepartment() {
+ return department;
+ }
+
+ public void setDepartment(DepartmentDTO department) {
+ this.department = department;
+ }
+
+ public LocalDate getDate() {
+ return date;
+ }
+
+ public void setDate(LocalDate date) {
+ this.date = date;
+ }
+
+ public LocalTime getTime() {
+ return time;
+ }
+
+ public void setTime(LocalTime time) {
+ this.time = time;
+ }
+
+ public String getDuration() {
+ return duration;
+ }
+
+ public void setDuration(String duration) {
+ this.duration = duration;
+ }
+
+ public String getPrecence() {
+ return precence;
+ }
+
+ public void setPrecence(String precence) {
+ this.precence = precence;
+ }
+
+ public List getPeriods() {
+ return periods;
+ }
+
+ public void setPeriods(List periods) {
+ this.periods = periods;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RosterRequestDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RosterRequestDTO.java
new file mode 100644
index 000000000..5e035ffc3
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RosterRequestDTO.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * DTO representing a tim-connector RosterRequest
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement(name = "export", namespace = "impexp.timn.aenova.nl")
+public class RosterRequestDTO {
+
+ @XmlElement
+ private DataDTO data;
+
+ public DataDTO getData() {
+ return data;
+ }
+
+ public void setData(DataDTO data) {
+ this.data = data;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RosterResponseDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RosterResponseDTO.java
new file mode 100644
index 000000000..7696f2cda
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/RosterResponseDTO.java
@@ -0,0 +1,51 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * DTO representing a tim-connector RosterResponse
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement(name = "exportResponse", namespace = "impexp.timn.aenova.nl")
+public class RosterResponseDTO {
+
+ @XmlElementWrapper(name = "return")
+ @XmlElement(name = "bezettingblok")
+ private List rosters;
+
+ public List getRosters() {
+ return rosters;
+ }
+
+ public void setRosters(List rosters) {
+ this.rosters = rosters;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimDateTimeAdapter.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimDateTimeAdapter.java
new file mode 100644
index 000000000..643d4aba7
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimDateTimeAdapter.java
@@ -0,0 +1,48 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+/**
+ * Adapter responsible for converting from DateTime to
+ * string(tim-string-datetime) and vice versa
+ *
+ * @author Miciele Ghiorghis
+ */
+public class TimDateTimeAdapter extends XmlAdapter {
+
+ @Override
+ public String marshal(DateTime dateTime) throws Exception {
+ return dateTime.toString("dd-MM-yyyy");
+ }
+
+ @Override
+ public DateTime unmarshal(String dateTimeStr) throws Exception {
+ DateTimeFormatter fmt = DateTimeFormat
+ .forPattern("dd-MM-yyyy HH:mm:ss.SSS");
+ return fmt.parseDateTime(dateTimeStr);
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimDoubleAdapter.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimDoubleAdapter.java
new file mode 100644
index 000000000..431f3c6d0
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimDoubleAdapter.java
@@ -0,0 +1,48 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import java.util.Locale;
+
+import javax.xml.bind.DatatypeConverter;
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+
+/**
+ * Adapter responsible for converting from Double to
+ * string(tim-string-double) and vice versa
+ *
+ * @author Miciele Ghiorghis
+ */
+public class TimDoubleAdapter extends XmlAdapter {
+
+ @Override
+ public String marshal(Double value) throws Exception {
+ if(value == null) {
+ return null;
+ }
+ return String.format(Locale.GERMAN, "%1$,.2f", value);
+ }
+
+ @Override
+ public Double unmarshal(String value) throws Exception {
+ return DatatypeConverter.parseDouble(value);
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimLocalDateAdapter.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimLocalDateAdapter.java
new file mode 100644
index 000000000..7edf3e3b9
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimLocalDateAdapter.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+
+import org.joda.time.LocalDate;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+/**
+ * Adapter responsible for converting from LocalDate to
+ * string(tim-string-date) and vice versa
+ *
+ * @author Miciele Ghiorghis
+ */
+public class TimLocalDateAdapter extends XmlAdapter {
+
+ @Override
+ public String marshal(LocalDate localDate) throws Exception {
+ return localDate.toString("dd-MM-yyyy");
+ }
+
+ @Override
+ public LocalDate unmarshal(String dateStr) throws Exception {
+ final DateTimeFormatter fmt = DateTimeFormat.forPattern("dd-MM-yyyy");
+ return fmt.parseDateTime(dateStr).toLocalDate();
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimOptions.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimOptions.java
new file mode 100644
index 000000000..8a0930a7b
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimOptions.java
@@ -0,0 +1,43 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+/**
+ * Class containing all constants for Tim-options.
+ *
+ * @author Miciele Ghiorghis
+ */
+public final class TimOptions {
+
+ private TimOptions() {
+
+ }
+
+ public static final String UPDATE_OR_INSERT = "@";
+ public static final String UPDATE = "%";
+ public static final String AUTO_INSERT = "!";
+ public static final String QUOTED = "''";
+ public static final String DECIMAL = "#";
+ public static final String SUM_DOUBLE = "+";
+ public static final String SUM_LONG = "&";
+ public static final String PARENT = "^";
+ public static final String ANY_PARENT = "~";
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimTimeAdapter.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimTimeAdapter.java
new file mode 100644
index 000000000..35f137ade
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimTimeAdapter.java
@@ -0,0 +1,44 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.adapters.XmlAdapter;
+
+import org.joda.time.LocalTime;
+
+/**
+ * Adapter responsible for converting from LocalTime to
+ * string(tim-string-time) and vice versa
+ *
+ * @author Miciele Ghiorghis
+ */
+public class TimTimeAdapter extends XmlAdapter {
+
+ @Override
+ public String marshal(LocalTime localTime) throws Exception {
+ return localTime.toString();
+ }
+
+ @Override
+ public LocalTime unmarshal(String localTimeStr) throws Exception {
+ return new LocalTime(localTimeStr);
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimeRegistrationDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimeRegistrationDTO.java
new file mode 100644
index 000000000..c4edc891e
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimeRegistrationDTO.java
@@ -0,0 +1,79 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * DTO representing a tim-connector TimeRegistration
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement(name = "tijdregistratie")
+public class TimeRegistrationDTO {
+
+ @XmlElement(name = "persoon")
+ private PersonDTO person;
+
+ private ProductDTO product;
+
+ @XmlElement(name = "datum")
+ private RegistrationDateDTO registrationDate;
+
+ @XmlElement(name = "duur")
+ private DurationDTO duration;
+
+ public PersonDTO getPerson() {
+ return person;
+ }
+
+ public void setPerson(PersonDTO person) {
+ this.person = person;
+ }
+
+ public ProductDTO getProduct() {
+ return product;
+ }
+
+ public void setProduct(ProductDTO product) {
+ this.product = product;
+ }
+
+ public RegistrationDateDTO getRegistrationDate() {
+ return registrationDate;
+ }
+
+ public void setRegistrationDate(RegistrationDateDTO registrationDate) {
+ this.registrationDate = registrationDate;
+ }
+
+ public DurationDTO getDuration() {
+ return duration;
+ }
+
+ public void setDuration(DurationDTO duration) {
+ this.duration = duration;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimeRegistrationRequestDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimeRegistrationRequestDTO.java
new file mode 100644
index 000000000..e49b53fdd
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimeRegistrationRequestDTO.java
@@ -0,0 +1,50 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * DTO representing a tim-connector TimeRegistrationRequest
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement(name = "import", namespace = "impexp.timn.aenova.nl")
+public class TimeRegistrationRequestDTO {
+
+ @XmlElementWrapper(name = "data")
+ @XmlElement(name = "tijdregistratie")
+ private List timeRegistrations;
+
+ public List getTimeRegistrations() {
+ return timeRegistrations;
+ }
+
+ public void setTimeRegistrations(List timeRegistrations) {
+ this.timeRegistrations = timeRegistrations;
+ }
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimeRegistrationResponseDTO.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimeRegistrationResponseDTO.java
new file mode 100644
index 000000000..23edc172a
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/TimeRegistrationResponseDTO.java
@@ -0,0 +1,51 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.importers.tim;
+
+import java.util.List;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlElementWrapper;
+import javax.xml.bind.annotation.XmlRootElement;
+
+/**
+ * DTO representing a tim-connector TimeRegistrationResponse
+ *
+ * @author Miciele Ghiorghis
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlRootElement(name = "importResponse", namespace = "impexp.timn.aenova.nl")
+public class TimeRegistrationResponseDTO {
+
+ @XmlElementWrapper(name = "return")
+ @XmlElement(name = "ref")
+ private List ref;
+
+ public List getRefs() {
+ return ref;
+ }
+
+ public void setRefs(List ref) {
+ this.ref = ref;
+ }
+
+}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/importers/tim/package-info.java b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/package-info.java
new file mode 100644
index 000000000..3e67d0999
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/importers/tim/package-info.java
@@ -0,0 +1,34 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * An xmlAdapaters that will be applied within this package
+ *
+ * @author Miciele Ghiorghis
+ */
+@javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters({
+ @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(type = DateTime.class, value = org.libreplan.importers.tim.TimDateTimeAdapter.class),
+ @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(type = LocalDate.class, value = org.libreplan.importers.tim.TimLocalDateAdapter.class),
+ @javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter(type = Date.class, value = org.libreplan.importers.tim.TimTimeAdapter.class) })
+package org.libreplan.importers.tim;
+
+import java.util.Date;
+
+import org.joda.time.DateTime;
+import org.joda.time.LocalDate;
diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java
index 09b2fa8e4..bef2292c9 100644
--- a/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java
+++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationController.java
@@ -30,6 +30,7 @@ import java.util.Comparator;
import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import javax.ws.rs.core.MediaType;
@@ -42,16 +43,20 @@ import org.apache.commons.logging.LogFactory;
import org.apache.cxf.jaxrs.client.WebClient;
import org.libreplan.business.calendars.entities.BaseCalendar;
import org.libreplan.business.common.entities.Configuration;
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.common.entities.ConnectorProperty;
import org.libreplan.business.common.entities.EntityNameEnum;
import org.libreplan.business.common.entities.EntitySequence;
-import org.libreplan.business.common.entities.JiraConfiguration;
import org.libreplan.business.common.entities.LDAPConfiguration;
import org.libreplan.business.common.entities.PersonalTimesheetsPeriodicityEnum;
+import org.libreplan.business.common.entities.PredefinedConnectorProperties;
+import org.libreplan.business.common.entities.PredefinedConnectors;
import org.libreplan.business.common.entities.ProgressType;
import org.libreplan.business.common.exceptions.ValidationException;
import org.libreplan.business.costcategories.entities.TypeOfWorkHours;
import org.libreplan.business.users.entities.UserRole;
import org.libreplan.importers.JiraRESTClient;
+import org.libreplan.importers.TimSoapClient;
import org.libreplan.web.common.components.bandboxsearch.BandboxSearch;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapTemplate;
@@ -83,6 +88,7 @@ import org.zkoss.zul.Rows;
import org.zkoss.zul.SimpleListModel;
import org.zkoss.zul.Textbox;
import org.zkoss.zul.api.Window;
+import org.zkoss.zul.impl.InputElement;
/**
* Controller for {@link Configuration} entity.
@@ -125,6 +131,12 @@ public class ConfigurationController extends GenericForwardComposer {
private Radiogroup strategy;
+ private Combobox connectorCombo;
+
+ private Grid connectorPropertriesGrid;
+
+ private Connector selectedConnector;
+
@Override
public void doAfterCompose(Component comp) throws Exception {
super.doAfterCompose(comp);
@@ -215,8 +227,16 @@ public class ConfigurationController extends GenericForwardComposer {
configurationModel.confirm();
configurationModel.init();
messages.showMessage(Level.INFO, _("Changes saved"));
+ if (getSelectedConnector() != null
+ && !configurationModel
+ .scheduleOrUnscheduleJobs(getSelectedConnector())) {
+ messages.showMessage(
+ Level.ERROR,
+ _("Scheduling or unscheduling of jobs for this connector is not completed"));
+ }
reloadWindow();
reloadEntitySequences();
+ reloadConnectors();
} catch (ValidationException e) {
messages.showInvalidValues(e);
} catch (ConcurrentModificationException e) {
@@ -224,6 +244,7 @@ public class ConfigurationController extends GenericForwardComposer {
configurationModel.init();
reloadWindow();
reloadEntitySequences();
+ reloadConnectors();
}
}
}
@@ -233,6 +254,7 @@ public class ConfigurationController extends GenericForwardComposer {
messages.showMessage(Level.INFO, _("Changes have been canceled"));
reloadWindow();
reloadEntitySequences();
+ reloadConnectors();
}
public void testLDAPConnection() {
@@ -267,22 +289,71 @@ public class ConfigurationController extends GenericForwardComposer {
}
/**
- * tests jira connection
+ * Tests connection
*/
- public void testJiraConnection() {
+ public void testConnection() {
+ if (selectedConnector == null) {
+ messages.showMessage(Level.ERROR,
+ _("Please select a connector to test it"));
+ return;
+ }
- JiraConfiguration jiraConfiguration = configurationModel
- .getJiraConfiguration();
+ Map properties = selectedConnector.getPropertiesAsMap();
+ String url = properties.get(PredefinedConnectorProperties.SERVER_URL);
+ String username = properties
+ .get(PredefinedConnectorProperties.USERNAME);
+ String password = properties
+ .get(PredefinedConnectorProperties.PASSWORD);
+
+ if (selectedConnector.getName().equals(
+ PredefinedConnectors.TIM.getName())) {
+ testTimConnection(url, username, password);
+ } else if (selectedConnector.getName().equals(
+ PredefinedConnectors.JIRA.getName())) {
+ testJiraConnection(url, username, password);
+ } else {
+ throw new RuntimeException("Unknown connector");
+ }
+ }
+
+ /**
+ * Test tim connection
+ *
+ * @param url
+ * the url of the server
+ * @param username
+ * the username
+ * @param password
+ * the password
+ */
+ private void testTimConnection(String url, String username, String password) {
+ if (TimSoapClient.checkAuthorization(url, username, password)) {
+ messages.showMessage(Level.INFO, _("Tim connection was successful"));
+ } else {
+ messages.showMessage(Level.ERROR, _("Cannot connet to Tim server"));
+ }
+ }
+
+ /**
+ * Test JIRA connection
+ *
+ * @param url
+ * the url
+ * @param username
+ * the username
+ * @param password
+ * the password
+ */
+ private void testJiraConnection(String url, String username, String password) {
try {
- WebClient client = WebClient.create(jiraConfiguration.getJiraUrl());
+ WebClient client = WebClient.create(url);
client.path(JiraRESTClient.PATH_AUTH_SESSION).accept(
MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML);
org.libreplan.ws.common.impl.Util.addAuthorizationHeader(client,
- jiraConfiguration.getJiraUserId(),
- jiraConfiguration.getJiraPassword());
+ username, password);
Response response = client.get();
@@ -290,17 +361,16 @@ public class ConfigurationController extends GenericForwardComposer {
messages.showMessage(Level.INFO,
_("JIRA connection was successful"));
} else {
- LOG.info("Status code: " + response.getStatus());
+ LOG.error("Status code: " + response.getStatus());
messages.showMessage(Level.ERROR,
_("Cannot connect to JIRA server"));
}
} catch (Exception e) {
- LOG.info(e);
+ LOG.error(e);
messages.showMessage(Level.ERROR,
_("Cannot connect to JIRA server"));
}
-
}
private boolean checkValidEntitySequenceRows() {
@@ -346,6 +416,14 @@ public class ConfigurationController extends GenericForwardComposer {
entitySequencesGrid.invalidate();
}
+ private void reloadConnectors() {
+ selectedConnector = configurationModel
+ .getConnectorByName(selectedConnector != null ? selectedConnector
+ .getName() : null);
+ Util.reloadBindings(connectorCombo);
+ Util.reloadBindings(connectorPropertriesGrid);
+ }
+
public String getCompanyCode() {
return configurationModel.getCompanyCode();
}
@@ -818,14 +896,6 @@ public class ConfigurationController extends GenericForwardComposer {
configurationModel.setLdapConfiguration(ldapConfiguration);
}
- public JiraConfiguration getJiraConfiguration() {
- return configurationModel.getJiraConfiguration();
- }
-
- public void setJiraConfiguration(JiraConfiguration jiraConfiguration) {
- configurationModel.setJiraConfiguration(jiraConfiguration);
- }
-
public RowRenderer getAllUserRolesRenderer() {
return new RowRenderer() {
@Override
@@ -976,12 +1046,105 @@ public class ConfigurationController extends GenericForwardComposer {
configurationModel.setSecondsPlanningWarning(secondsPlanningWarning);
}
- public TypeOfWorkHours getJiraConnectorTypeOfWorkHours() {
- return configurationModel.getJiraConnectorTypeOfWorkHours();
+ public List getConnectors() {
+ return configurationModel.getConnectors();
}
- public void setJiraConnectorTypeOfWorkHours(TypeOfWorkHours typeOfWorkHours) {
- configurationModel.setJiraConnectorTypeOfWorkHours(typeOfWorkHours);
+ public Connector getSelectedConnector() {
+ return selectedConnector;
+ }
+
+ public void setSelectedConnector(Connector connector) {
+ selectedConnector = connector;
+ Util.reloadBindings(connectorPropertriesGrid);
+ }
+
+ public List getConnectorPropertries() {
+ if (selectedConnector == null) {
+ return Collections.emptyList();
+ }
+ return selectedConnector.getProperties();
+ }
+
+ public RowRenderer getConnectorPropertriesRenderer() {
+ return new RowRenderer() {
+ @Override
+ public void render(Row row, Object data) {
+ ConnectorProperty property = (ConnectorProperty) data;
+ row.setValue(property);
+
+ Util.appendLabel(row, _(property.getKey()));
+ appendValueTextbox(row, property);
+ }
+
+ private void appendValueTextbox(Row row,
+ final ConnectorProperty property) {
+ final Textbox textbox = new Textbox();
+ textbox.setWidth("400px");
+ textbox.setConstraint(checkPropertyValue(property));
+
+ Util.bind(textbox, new Util.Getter() {
+
+ @Override
+ public String get() {
+ return property.getValue();
+ }
+ }, new Util.Setter() {
+
+ @Override
+ public void set(String value) {
+ property.setValue(value);
+ }
+ });
+ if (property.getKey().equals(
+ PredefinedConnectorProperties.PASSWORD)) {
+ textbox.setType("password");
+ }
+
+ row.appendChild(textbox);
+ }
+
+ public Constraint checkPropertyValue(
+ final ConnectorProperty property) {
+ final String key = property.getKey();
+ return new Constraint() {
+ @Override
+ public void validate(Component comp, Object value) {
+ if (key.equals(PredefinedConnectorProperties.ACTIVATED)) {
+ if (!((String) value).equalsIgnoreCase("Y")
+ && !((String) value).equalsIgnoreCase("N")) {
+ throw new WrongValueException(comp, _(
+ "Only {0} allowed", "Y/N"));
+ }
+ } else if (key
+ .equals(PredefinedConnectorProperties.SERVER_URL)
+ || key.equals(PredefinedConnectorProperties.USERNAME)
+ || key.equals(PredefinedConnectorProperties.PASSWORD)
+ || key.equals(PredefinedConnectorProperties.JIRA_HOURS_TYPE)) {
+ ((InputElement) comp).setConstraint("no empty:"
+ + _("cannot be empty"));
+ } else if (key
+ .equals(PredefinedConnectorProperties.TIM_NR_DAYS_TIMESHEET)
+ || key.equals(PredefinedConnectorProperties.TIM_NR_DAYS_ROSTER)) {
+ if (!isNumeric((String) value)) {
+ throw new WrongValueException(comp,
+ _("Only digits allowed"));
+ }
+ }
+ }
+ };
+ }
+
+ private boolean isNumeric(String input) {
+ try {
+ Integer.parseInt(input);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ };
}
}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationModel.java
index bb0fd97b1..fdbcada4c 100644
--- a/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationModel.java
+++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/ConfigurationModel.java
@@ -25,6 +25,7 @@ import static org.libreplan.web.I18nHelper._;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Currency;
import java.util.HashMap;
import java.util.HashSet;
@@ -38,11 +39,12 @@ import org.apache.commons.lang.StringUtils;
import org.libreplan.business.calendars.daos.IBaseCalendarDAO;
import org.libreplan.business.calendars.entities.BaseCalendar;
import org.libreplan.business.common.daos.IConfigurationDAO;
+import org.libreplan.business.common.daos.IConnectorDAO;
import org.libreplan.business.common.daos.IEntitySequenceDAO;
import org.libreplan.business.common.entities.Configuration;
+import org.libreplan.business.common.entities.Connector;
import org.libreplan.business.common.entities.EntityNameEnum;
import org.libreplan.business.common.entities.EntitySequence;
-import org.libreplan.business.common.entities.JiraConfiguration;
import org.libreplan.business.common.entities.LDAPConfiguration;
import org.libreplan.business.common.entities.PersonalTimesheetsPeriodicityEnum;
import org.libreplan.business.common.entities.ProgressType;
@@ -76,6 +78,8 @@ public class ConfigurationModel implements IConfigurationModel {
private static Map currencies = getAllCurrencies();
+ private List connectors;
+
@Autowired
private IConfigurationDAO configurationDAO;
@@ -88,6 +92,12 @@ public class ConfigurationModel implements IConfigurationModel {
@Autowired
private IWorkReportDAO workReportDAO;
+ @Autowired
+ private IConnectorDAO connectorDAO;
+
+ @Autowired
+ private IJobSchedulerModel jobSchedulerModel;
+
@Override
@Transactional(readOnly = true)
public List getCalendars() {
@@ -107,6 +117,8 @@ public class ConfigurationModel implements IConfigurationModel {
public void init() {
this.configuration = getCurrentConfiguration();
initEntitySequences();
+ initLdapConfiguration();
+ initConnectorConfiguration();
}
private void initEntitySequences() {
@@ -120,6 +132,23 @@ public class ConfigurationModel implements IConfigurationModel {
}
}
+ private void initLdapConfiguration() {
+ if (null == configuration.getLdapConfiguration()) {
+ configuration.setLdapConfiguration(LDAPConfiguration.create());
+ }
+ }
+
+ private void initConnectorConfiguration() {
+ connectors = connectorDAO.getAll();
+ forceLoadConnectors();
+ }
+
+ private void forceLoadConnectors() {
+ for (Connector connector : connectors) {
+ connector.getProperties().size();
+ }
+ }
+
private Configuration getCurrentConfiguration() {
Configuration configuration = configurationDAO.getConfiguration();
if (configuration == null) {
@@ -132,8 +161,6 @@ public class ConfigurationModel implements IConfigurationModel {
private void forceLoad(Configuration configuration) {
forceLoad(configuration.getDefaultCalendar());
forceLoad(configuration.getPersonalTimesheetsTypeOfWorkHours());
- forceLoad(configuration.getJiraConfiguration()
- .getJiraConnectorTypeOfWorkHours());
}
private void forceLoad(BaseCalendar calendar) {
@@ -160,6 +187,7 @@ public class ConfigurationModel implements IConfigurationModel {
public void confirm() {
checkEntitySequences();
configurationDAO.save(configuration);
+ saveConnectors();
try {
storeAndRemoveEntitySequences();
} catch (IllegalStateException e) {
@@ -663,36 +691,34 @@ public class ConfigurationModel implements IConfigurationModel {
configuration.setSecondsPlanningWarning(secondsPlanningWarning);
}
- @Override
- public void setJiraConfiguration(JiraConfiguration jiraConfiguration) {
- configuration.setJiraConfiguration(jiraConfiguration);
+ private void saveConnectors() {
+ for (Connector connector : connectors) {
+ connectorDAO.save(connector);
+ }
}
@Override
- public JiraConfiguration getJiraConfiguration() {
- return configuration.getJiraConfiguration();
+ public List getConnectors() {
+ return Collections.unmodifiableList(connectors);
}
@Override
- public TypeOfWorkHours getJiraConnectorTypeOfWorkHours() {
- JiraConfiguration jiraConfiguration = configuration
- .getJiraConfiguration();
- if (jiraConfiguration != null) {
- return jiraConfiguration.getJiraConnectorTypeOfWorkHours();
+ public Connector getConnectorByName(String name) {
+ if (name == null || connectors == null) {
+ return null;
+ }
+
+ for (Connector connector : connectors) {
+ if (connector.getName().equals(name)) {
+ return connector;
+ }
}
return null;
}
@Override
- public void setJiraConnectorTypeOfWorkHours(TypeOfWorkHours typeOfWorkHours) {
- if (configuration != null) {
- JiraConfiguration jiraConfiguration = configuration
- .getJiraConfiguration();
- if (jiraConfiguration != null) {
- jiraConfiguration
- .setJiraConnectorTypeOfWorkHours(typeOfWorkHours);
- }
- }
+ public boolean scheduleOrUnscheduleJobs(Connector connector) {
+ return jobSchedulerModel.scheduleOrUnscheduleJobs(connector);
}
}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/CustomMenuController.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/CustomMenuController.java
index d0806ff64..f1aad8ca4 100644
--- a/libreplan-webapp/src/main/java/org/libreplan/web/common/CustomMenuController.java
+++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/CustomMenuController.java
@@ -414,6 +414,13 @@ public class CustomMenuController extends Div implements IMenuItemsRegister {
"/profiles/profiles.zul",
"13-usuarios.html#administraci-n-de-perfiles"));
}
+ if (SecurityUtils
+ .isSuperuserOrUserInRoles(UserRole.ROLE_JOB_SCHEDULING)) {
+ configurationItems.add(subItem(_("Job Scheduling"),
+ "/common/job_scheduling.zul",
+ "16-ldap-authentication.html"));
+ }
+
if (!configurationItems.isEmpty()) {
topItem(_("Configuration"), "/common/configuration.zul", "",
configurationItems);
diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/IConfigurationModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/IConfigurationModel.java
index 71ffc5aca..c3f488355 100644
--- a/libreplan-webapp/src/main/java/org/libreplan/web/common/IConfigurationModel.java
+++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/IConfigurationModel.java
@@ -25,9 +25,9 @@ import java.util.List;
import java.util.Set;
import org.libreplan.business.calendars.entities.BaseCalendar;
+import org.libreplan.business.common.entities.Connector;
import org.libreplan.business.common.entities.EntityNameEnum;
import org.libreplan.business.common.entities.EntitySequence;
-import org.libreplan.business.common.entities.JiraConfiguration;
import org.libreplan.business.common.entities.LDAPConfiguration;
import org.libreplan.business.common.entities.PersonalTimesheetsPeriodicityEnum;
import org.libreplan.business.common.entities.ProgressType;
@@ -186,12 +186,10 @@ public interface IConfigurationModel {
void setSecondsPlanningWarning(
Integer planningWarningExitWithoutSavingSeconds);
- void setJiraConfiguration(JiraConfiguration jiraConfiguration);
+ List getConnectors();
- JiraConfiguration getJiraConfiguration();
+ Connector getConnectorByName(String name);
- TypeOfWorkHours getJiraConnectorTypeOfWorkHours();
-
- void setJiraConnectorTypeOfWorkHours(TypeOfWorkHours typeOfWorkHours);
+ boolean scheduleOrUnscheduleJobs(Connector connector);
}
diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java
new file mode 100644
index 000000000..c8043e533
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/IJobSchedulerModel.java
@@ -0,0 +1,149 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 Igalia, S.L.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.web.common;
+
+import java.util.List;
+
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.common.entities.ConnectorException;
+import org.libreplan.business.common.entities.JobSchedulerConfiguration;
+import org.libreplan.business.common.entities.PredefinedConnectorProperties;
+import org.libreplan.business.common.exceptions.ValidationException;
+import org.libreplan.importers.SynchronizationInfo;
+
+/**
+ * Contract for {@link JobSchedulerModel}.
+ *
+ * @author Manuel Rego Casasnovas
+ * @author Miciele Ghiorghis
+ */
+public interface IJobSchedulerModel {
+
+ /**
+ * returns all job scheduler configurations
+ *
+ * @return list of JobSchedulerConfiguration
+ */
+ List getJobSchedulerConfigurations();
+
+ /**
+ * returns next fire time for the specified job from
+ * {@link JobSchedulerConfiguration}
+ *
+ * @param jobSchedulerConfiguration
+ * the job scheduler configuration
+ */
+ String getNextFireTime(JobSchedulerConfiguration jobSchedulerConfiguration);
+
+ /**
+ * Do manual action(replacement of scheduling)
+ *
+ * @param jobSchedulerConfiguration
+ * the job configuration
+ * @throws ConnectorException
+ * if connector is not valid
+ */
+ void doManual(JobSchedulerConfiguration jobSchedulerConfiguration)
+ throws ConnectorException;
+
+ /**
+ * Returns synchronization infos. Failures or successes info
+ */
+ List getSynchronizationInfos();
+
+ /**
+ * Prepares for create a new {@link JobSchedulerConfiguration}.
+ */
+ void initCreate();
+
+ /**
+ * Prepares for edit {@link JobSchedulerConfiguration}
+ *
+ * @param jobSchedulerConfiguration
+ * object to be edited
+ */
+ void initEdit(JobSchedulerConfiguration jobSchedulerConfiguration);
+
+ /**
+ * Gets the current {@link JobSchedulerConfiguration}.
+ *
+ * @return A {@link JobSchedulerConfiguration}
+ */
+ JobSchedulerConfiguration getJobSchedulerConfiguration();
+
+ /**
+ * Saves the current {@link JobSchedulerConfiguration}
+ *
+ * @throws ValidationException
+ * if validation fails
+ */
+ void confirmSave() throws ValidationException;
+
+ /**
+ * Cancels the current {@link JobSchedulerConfiguration}
+ */
+ void cancel();
+
+ /**
+ * Removes the current {@link JobSchedulerConfiguration}
+ *
+ * @param jobSchedulerConfiguration
+ * object to be removed
+ */
+ void remove(JobSchedulerConfiguration jobSchedulerConfiguration);
+
+ /**
+ * returns list of connectors
+ */
+ List getConnectors();
+
+ /**
+ * Schedule or unschedule jobs for the specified connector
+ *
+ * schedule all jobs of the specified connector's property
+ * {@link PredefinedConnectorProperties#ACTIVATED} is 'Y', otherwise
+ * unschedule the jobs
+ *
+ * @param connector
+ * where to check if property is changed
+ * @return true if (un)scheduling is successful, false otherwise
+ */
+ boolean scheduleOrUnscheduleJobs(Connector connector);
+
+ /**
+ * schedule or unschedule job for the specified job in
+ * {@link JobSchedulerConfiguration}
+ *
+ * @return true if scheduling is succeeded, false otherwise
+ */
+ boolean scheduleOrUnscheduleJob();
+
+ /**
+ * Delete job specified in {@link JobSchedulerConfiguration}
+ *
+ * @param jobSchedulerConfiguration
+ * configuration for the job to be deleted
+ * @return true if job is successfully deleted from the scheduler, false
+ * otherwise
+ */
+ boolean deleteScheduledJob(
+ JobSchedulerConfiguration jobSchedulerConfiguration);
+
+}
\ No newline at end of file
diff --git a/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerController.java b/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerController.java
new file mode 100644
index 000000000..0199cdacb
--- /dev/null
+++ b/libreplan-webapp/src/main/java/org/libreplan/web/common/JobSchedulerController.java
@@ -0,0 +1,438 @@
+/*
+ * This file is part of LibrePlan
+ *
+ * Copyright (C) 2013 St. Antoniusziekenhuis
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package org.libreplan.web.common;
+
+import static org.libreplan.web.I18nHelper._;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.libreplan.business.common.entities.Connector;
+import org.libreplan.business.common.entities.ConnectorException;
+import org.libreplan.business.common.entities.JobClassNameEnum;
+import org.libreplan.business.common.entities.JobSchedulerConfiguration;
+import org.libreplan.business.common.exceptions.InstanceNotFoundException;
+import org.libreplan.business.common.exceptions.ValidationException;
+import org.libreplan.importers.SynchronizationInfo;
+import org.quartz.CronExpression;
+import org.zkoss.zk.ui.Component;
+import org.zkoss.zk.ui.Executions;
+import org.zkoss.zk.ui.SuspendNotAllowedException;
+import org.zkoss.zk.ui.WrongValueException;
+import org.zkoss.zk.ui.event.Event;
+import org.zkoss.zk.ui.event.EventListener;
+import org.zkoss.zk.ui.event.Events;
+import org.zkoss.zul.Button;
+import org.zkoss.zul.Grid;
+import org.zkoss.zul.Groupbox;
+import org.zkoss.zul.Hbox;
+import org.zkoss.zul.Label;
+import org.zkoss.zul.Listbox;
+import org.zkoss.zul.Popup;
+import org.zkoss.zul.Row;
+import org.zkoss.zul.RowRenderer;
+import org.zkoss.zul.SimpleListModel;
+import org.zkoss.zul.api.Caption;
+import org.zkoss.zul.api.Textbox;
+import org.zkoss.zul.api.Window;
+
+/**
+ * Controller for job scheduler manager
+ *
+ * @author Miciele Ghiorghis
+ */
+public class JobSchedulerController extends
+ BaseCRUDController {
+
+ private static final Log LOG = LogFactory
+ .getLog(JobSchedulerController.class);
+
+ private Grid listJobSchedulings;
+ private Grid cronExpressionGrid;
+
+ private Popup cronExpressionInputPopup;
+
+ private Label jobGroup;
+ private Label jobName;
+
+ private Textbox cronExpressionTextBox;
+ private Textbox cronExpressionSeconds;
+ private Textbox cronExpressionMinutes;
+ private Textbox cronExpressionHours;
+ private Textbox cronExpressionDayOfMonth;
+ private Textbox cronExpressionMonth;
+ private Textbox cronExpressionDayOfWeek;
+ private Textbox cronExpressionYear;
+
+ private IJobSchedulerModel jobSchedulerModel;
+
+ @Override
+ public void doAfterCompose(Component comp) throws Exception {
+ super.doAfterCompose(comp);
+ listJobSchedulings = (Grid) listWindow
+ .getFellowIfAny("listJobSchedulings");
+ listJobSchedulings.getModel();
+ initCronExpressionPopup();
+ }
+
+ /**
+ * initializes cron expressions for popup
+ */
+ private void initCronExpressionPopup() {
+ cronExpressionTextBox = (Textbox) editWindow
+ .getFellow("cronExpressionTextBox");
+
+ cronExpressionInputPopup = (Popup) editWindow
+ .getFellow("cronExpressionInputPopup");
+
+ jobGroup = (Label) cronExpressionInputPopup.getFellow("jobGroup");
+ jobName = (Label) cronExpressionInputPopup.getFellow("jobName");
+
+ cronExpressionGrid = (Grid) cronExpressionInputPopup
+ .getFellow("cronExpressionGrid");
+
+ cronExpressionSeconds = (Textbox) cronExpressionGrid
+ .getFellow("cronExpressionSeconds");
+ cronExpressionMinutes = (Textbox) cronExpressionGrid
+ .getFellow("cronExpressionMinutes");
+ cronExpressionHours = (Textbox) cronExpressionGrid
+ .getFellow("cronExpressionHours");
+ cronExpressionDayOfMonth = (Textbox) cronExpressionGrid
+ .getFellow("cronExpressionDayOfMonth");
+ cronExpressionMonth = (Textbox) cronExpressionGrid
+ .getFellow("cronExpressionMonth");
+ cronExpressionDayOfWeek = (Textbox) cronExpressionGrid
+ .getFellow("cronExpressionDayOfWeek");
+ cronExpressionYear = (Textbox) cronExpressionGrid
+ .getFellow("cronExpressionYear");
+ }
+
+ /**
+ * returns a list of {@link JobSchedulerConfiguration}
+ */
+ public List getJobSchedulerConfigurations() {
+ return jobSchedulerModel.getJobSchedulerConfigurations();
+ }
+
+ /**
+ * returns {@link JobSchedulerConfiguration}
+ */
+ public JobSchedulerConfiguration getJobSchedulerConfiguration() {
+ return jobSchedulerModel.getJobSchedulerConfiguration();
+ }
+
+ /**
+ * returns all predefined jobs
+ */
+ public JobClassNameEnum[] getJobNames() {
+ return JobClassNameEnum.values();
+ }
+
+ /**
+ * return list of connectorNames
+ */
+ public List getConnectorNames() {
+ List connectors = jobSchedulerModel.getConnectors();
+ List