diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/IntegrationEntity.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/IntegrationEntity.java index ba7046365..8c070e774 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/common/IntegrationEntity.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/IntegrationEntity.java @@ -20,6 +20,8 @@ package org.navalplanner.business.common; +import java.util.HashSet; +import java.util.Set; import java.util.UUID; import org.apache.commons.lang.StringUtils; @@ -123,6 +125,35 @@ public abstract class IntegrationEntity extends BaseEntity { } + /** + * It returns the first repeated code in the entities received as a + * parameter. If none is repeated, it returns null. + * Concrete entities may use this method to implement validation rules + * for detecting repeated codes in dependent entities. + */ + protected String getFirstRepeatedCode( + Set entities) { + + Set codes = new HashSet(); + + for (IntegrationEntity e : entities) { + + String code = e.getCode(); + + if (!StringUtils.isBlank(code)) { + if (codes.contains(code.toLowerCase())) { + return code; + } else { + codes.add(code.toLowerCase()); + } + } + + } + + return null; + + } + /** * It returns the DAO of this entity. */ diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/daos/IIntegrationEntityDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/daos/IIntegrationEntityDAO.java index 3a0db691f..862470b38 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/common/daos/IIntegrationEntityDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/daos/IIntegrationEntityDAO.java @@ -20,6 +20,8 @@ package org.navalplanner.business.common.daos; +import java.util.List; + import org.navalplanner.business.common.IntegrationEntity; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; @@ -60,4 +62,9 @@ public interface IIntegrationEntityDAO public E findExistingEntityByCode(String code); + /** + * It returns all entities ordered by ascending code. + */ + public List findAll(); + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/common/daos/IntegrationEntityDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/common/daos/IntegrationEntityDAO.java index 2c65dca70..8317def3c 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/common/daos/IntegrationEntityDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/common/daos/IntegrationEntityDAO.java @@ -20,7 +20,10 @@ package org.navalplanner.business.common.daos; +import java.util.List; + import org.apache.commons.lang.StringUtils; +import org.hibernate.criterion.Order; import org.hibernate.criterion.Restrictions; import org.navalplanner.business.common.IntegrationEntity; import org.navalplanner.business.common.exceptions.InstanceNotFoundException; @@ -95,4 +98,11 @@ public class IntegrationEntityDAO } + @SuppressWarnings("unchecked") + @Override + public List findAll() { + return getSession().createCriteria(getEntityClass()). + addOrder(Order.asc("code")).list(); + } + } diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/CriterionTypeDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/CriterionTypeDAO.java index a1786aef9..3743bbc9a 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/CriterionTypeDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/CriterionTypeDAO.java @@ -45,22 +45,6 @@ import org.springframework.transaction.annotation.Transactional; public class CriterionTypeDAO extends IntegrationEntityDAO implements ICriterionTypeDAO { - @Override - @Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW) - public CriterionType findByCodeAnotherTransactionInitialized( - String code) throws InstanceNotFoundException { - - CriterionType criterionType = findByCode(code); - - for (Criterion c : criterionType.getCriterions()) { - c.getChildren().size(); - c.getType().getName(); - } - - return criterionType; - - } - @Override public List findByName(CriterionType criterionType) { Criteria c = getSession().createCriteria(CriterionType.class); diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/ICriterionTypeDAO.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/ICriterionTypeDAO.java index beb1cdf9d..e39f93767 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/ICriterionTypeDAO.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/daos/ICriterionTypeDAO.java @@ -36,9 +36,6 @@ import org.navalplanner.business.resources.entities.ResourceEnum; public interface ICriterionTypeDAO extends IIntegrationEntityDAO { - CriterionType findByCodeAnotherTransactionInitialized(String code) - throws InstanceNotFoundException; - CriterionType findUniqueByName(String name) throws InstanceNotFoundException; diff --git a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionType.java b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionType.java index 81129edb8..5cd7e2346 100644 --- a/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionType.java +++ b/navalplanner-business/src/main/java/org/navalplanner/business/resources/entities/CriterionType.java @@ -346,6 +346,12 @@ public class CriterionType extends IntegrationEntity implements } + @AssertTrue(message="criterion codes must be unique inside a criterion " + + "type") + public boolean checkConstraintNonRepeatedCriterionCodes() { + return getFirstRepeatedCode(criterions) == null; + } + @AssertTrue(message="criterion names must be unique inside a criterion " + "type") public boolean checkConstraintNonRepeatedCriterionNames() { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/DuplicateCodeBeingImportedException.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/DuplicateCodeBeingImportedException.java deleted file mode 100644 index 16b9cd72c..000000000 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/DuplicateCodeBeingImportedException.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of NavalPlan - * - * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e - * Desenvolvemento Tecnolóxico de Galicia - * - * 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.navalplanner.ws.common.api; - -/** - * An exception for notifying that an instance is already being imported in - * a bulk import because it has a code used by another instance of the same - * type. - * - * @author Fernando Bellas Permuy - */ -@SuppressWarnings("serial") -public class DuplicateCodeBeingImportedException - extends DuplicateInstanceBeingImportedException { - - private String code; - - public DuplicateCodeBeingImportedException(String instanceType, - String code) { - - super(instanceType); - this.code = code; - } - - public String getCode() { - return code; - } - -} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/DuplicateInstanceBeingImportedException.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/DuplicateInstanceBeingImportedException.java deleted file mode 100644 index 26727d8cd..000000000 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/DuplicateInstanceBeingImportedException.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This file is part of NavalPlan - * - * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e - * Desenvolvemento Tecnolóxico de Galicia - * - * 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.navalplanner.ws.common.api; - -/** - * An exception for notifying that an instance is already being imported in - * a bulk import. - * - * @author Fernando Bellas Permuy - */ -@SuppressWarnings("serial") -public class DuplicateInstanceBeingImportedException extends Exception { - - private String entityType; - - public DuplicateInstanceBeingImportedException(String entityType) { - this.entityType = entityType; - } - - public String getEntityType() { - return entityType; - } - -} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/DuplicateNaturalKeyBeingImportedException.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/DuplicateNaturalKeyBeingImportedException.java deleted file mode 100644 index 9e1a4bbe4..000000000 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/DuplicateNaturalKeyBeingImportedException.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This file is part of NavalPlan - * - * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e - * Desenvolvemento Tecnolóxico de Galicia - * - * 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.navalplanner.ws.common.api; - -/** - * An exception for notifying that an instance is already being imported in - * a bulk import because it has a natural key used by another instance of the - * same type. - * - * @author Fernando Bellas Permuy - */ -@SuppressWarnings("serial") -public class DuplicateNaturalKeyBeingImportedException - extends DuplicateInstanceBeingImportedException { - - private String[] naturalKeyValues; - - public DuplicateNaturalKeyBeingImportedException(String instanceType, - String[] naturalKeyValues) { - - super(instanceType); - this.naturalKeyValues = naturalKeyValues; - - } - - public String[] getNaturalKeyValues() { - return naturalKeyValues; - } - -} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/InstanceConstraintViolationsDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/InstanceConstraintViolationsDTO.java index 42a105b82..92bdb771f 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/InstanceConstraintViolationsDTO.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/InstanceConstraintViolationsDTO.java @@ -27,6 +27,7 @@ import java.util.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; /** * DTO for modeling the list of constraint violations on a given instance. @@ -56,9 +57,13 @@ public class InstanceConstraintViolationsDTO { @XmlAttribute(name=ENTITY_TYPE_ATTRIBUTE_NAME) public String entityType; + @XmlElementWrapper(name="constraint-violations") @XmlElement(name="constraint-violation") public List constraintViolations; + @XmlElement(name="internal-error") + public InternalErrorDTO internalError; + public InstanceConstraintViolationsDTO() {} @Deprecated @@ -70,17 +75,33 @@ public class InstanceConstraintViolationsDTO { } - public InstanceConstraintViolationsDTO( - InstanceConstraintViolationsDTOId instanceId, - List constraintViolations) { + private InstanceConstraintViolationsDTO( + InstanceConstraintViolationsDTOId instanceId) { this.numItem = instanceId.getNumItem(); this.code = instanceId.getCode(); this.entityType = instanceId.getEntityType(); + + } + + public InstanceConstraintViolationsDTO( + InstanceConstraintViolationsDTOId instanceId, + List constraintViolations) { + + this(instanceId); this.constraintViolations = constraintViolations; } + public InstanceConstraintViolationsDTO( + InstanceConstraintViolationsDTOId instanceId, + InternalErrorDTO internalError) { + + this(instanceId); + this.internalError = internalError; + + } + @Deprecated public static InstanceConstraintViolationsDTO create(String instanceId, String message) { diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/IntegrationEntityDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/IntegrationEntityDTO.java index 266fdca25..44b632f20 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/IntegrationEntityDTO.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/api/IntegrationEntityDTO.java @@ -20,28 +20,13 @@ package org.navalplanner.ws.common.api; -import java.util.Set; import java.util.UUID; import javax.xml.bind.annotation.XmlAttribute; -import org.apache.commons.lang.StringUtils; - /** * DTO for IntegrationEntity. All DTOs corresponding to entities * to be used in application integration must extend from this DTO. - *
- *
- * All entities must redefine getEntityType(). Additionally, - * entities that can be bulk imported may need to redefine the following - * methods (ordered by probability of being redefined): - *
    - *
  • getNaturalKeyValues().
  • - *
  • checkDuplicateCode().
  • - *
  • getUniversalCodePrefix().
  • - *
  • getUniversalNaturalKeyPrefix().
  • - *
  • checkDuplicateNaturalKey().
  • - *
* * @author Fernando Bellas Permuy */ @@ -64,93 +49,6 @@ public abstract class IntegrationEntityDTO { */ public abstract String getEntityType(); - /** - * It returns the prefix specified in getUniversalCode(). The - * default implementation returns the same value as - * getEntityType(). Entities that can be bulk imported, and - * that have derived entities, must redefine this method to return the - * same String for all child entities if they share the code space. - */ - protected String getUniversalCodePrefix() { - return getEntityType(); - } - - /** - * It returns the prefix specified in getUniversalNaturalKey(). - * The default implementation returns the same value as - * getEntityType(). Entities that can be bulk imported, and - * that have derived entities, must redefine this method to return the - * same String for all child entities if they share the natural key space. - */ - protected String getUniversalNaturalKeyPrefix() { - return getEntityType(); - } - - /** - * It returns the values (as String) of the fields that represent the - * natural key. If the entity has no natural key fields, it must return - * null or an empty array. The default implementation - * returns null. Entities, with natural key, that can be bulk - * imported must redefine this method. - */ - protected String[] getNaturalKeyValues() { - return null; - } - - /** - * It checks if this entity or one its contained entities has a code - * (the entity code is obtained by calling on - * getUniversalCode()) contained in the set of keys passed as - * a parameter. If the code is not contained, it is added to the set of - * keys. No other class should manipulate this set. Comparison is case - * insensitive and leading and trailing whitespace is discarded. If the - * code is whitespace, empty ("") or null, the check is - * considered OK (and the code is not added to the set of existing keys). - *
- *
- * The default implementation only provides this semantics for the - * container (this) entity. Entities, with contained entities, that can be - * bulk imported must redefine this method to apply it recursively to their - * contained entities. - */ - public void checkDuplicateCode(Set existingKeys) - throws DuplicateCodeBeingImportedException { - - if (containsKeyAndAdd(existingKeys, getUniversalCode())) { - throw new DuplicateCodeBeingImportedException(getEntityType(), - code); - } - - } - - /** - * It checks if this entity or one its contained entities has a natural key - * (the entity natural key is obtained by calling on - * getUniversalNaturalKey()) contained in the set of keys - * passed as a parameter. If the natural key is not contained, it is added - * to the set of keys. No other class should manipulate this set. - * Comparison is case insensitive and leading and trailing whitespace is - * discarded. If some value of the natural key is whitespace, empty ("") or - * null, the check is considered OK (and the natural key is - * not added to the set of existing keys). - *
- *
- * The default implementation only provides this semantics for the - * container entity. Entities that can be bulk imported could be interested - * in redefining this method. However, the default implementation is - * probably good enough for them, since probably only the container - * entities will have natural keys. - */ - public void checkDuplicateNaturalKey(Set existingKeys) - throws DuplicateNaturalKeyBeingImportedException { - - if (containsKeyAndAdd(existingKeys, getUniversalNaturalKey())) { - throw new DuplicateNaturalKeyBeingImportedException(getEntityType(), - getNaturalKeyValues()); - } - - } - /** * This method is useful to implement constructors (in subclasses) that * automatically generate a unique code. Such constructors are useful for @@ -161,76 +59,4 @@ public abstract class IntegrationEntityDTO { return UUID.randomUUID().toString(); } - /** - * It returns a String with the format - * getUniversalCodePrefix().code.<>, or - * null if code is whitespace, empty ("") or - * null. - */ - private String getUniversalCode() { - - if (!StringUtils.isBlank(code)) { - return getUniversalCodePrefix() + ".code." + StringUtils.trim(code); - } else { - return null; - } - - } - - /** - * It returns a String with the format - * getUniversalNaturalKeyPrefix().naturalkey.<>, - * or null if some natural key value is whitespace, empty ("") - * or null. - */ - protected final String getUniversalNaturalKey() { - - String[] naturalKeyValues = getNaturalKeyValues(); - - if (naturalKeyValues == null || naturalKeyValues.length == 0) { - return null; - } - - String universalNaturalKey = getUniversalNaturalKeyPrefix() + - ".natural-key."; - - for (String k : naturalKeyValues) { - - if (StringUtils.isBlank(k)) { - return null; - } - - universalNaturalKey += StringUtils.trim(k); - - } - - return universalNaturalKey; - - } - - /** - * It checks if a key is contained in a set of existing keys. If not - * contained, the key is added to the set of existing keys. Comparison is - * case insensitive and leading and trailing whitespace is discarded. If - * the key is whitespace, empty ("") or null, it - * returns false (and the key is not added to the set of - * existing keys). - */ - protected boolean containsKeyAndAdd(Set existingKeys, String newKey) { - - if (StringUtils.isBlank(newKey)) { - return false; - } - - String key = StringUtils.trim(newKey).toLowerCase(); - - if (!existingKeys.contains(key)) { - existingKeys.add(key); - return false; - } - - return true; - - } - } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/impl/ConstraintViolationConverter.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/impl/ConstraintViolationConverter.java index db4498def..853d91bd8 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/impl/ConstraintViolationConverter.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/impl/ConstraintViolationConverter.java @@ -28,6 +28,7 @@ import org.navalplanner.business.common.exceptions.ValidationException; import org.navalplanner.ws.common.api.ConstraintViolationDTO; import org.navalplanner.ws.common.api.InstanceConstraintViolationsDTO; import org.navalplanner.ws.common.api.InstanceConstraintViolationsDTOId; +import org.navalplanner.ws.common.api.InternalErrorDTO; /** * Converter for constraint violations. @@ -114,4 +115,17 @@ public class ConstraintViolationConverter { } + public final static InstanceConstraintViolationsDTO toDTO( + InstanceConstraintViolationsDTOId instanceId, + RuntimeException runtimeException) { + + InternalErrorDTO internalErrorDTO = new InternalErrorDTO( + runtimeException.getMessage(), + Util.getStackTrace(runtimeException)); + + return new InstanceConstraintViolationsDTO(instanceId, + internalErrorDTO); + + } + } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/impl/GenericRESTService.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/impl/GenericRESTService.java new file mode 100644 index 000000000..d65d0e1be --- /dev/null +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/common/impl/GenericRESTService.java @@ -0,0 +1,183 @@ +/* + * This file is part of ###PROJECT_NAME### + * + * Copyright (C) 2009 Fundación para o Fomento da Calidade Industrial e + * Desenvolvemento Tecnolóxico de Galicia + * + * 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.navalplanner.ws.common.impl; + +import java.util.ArrayList; +import java.util.List; + +import org.navalplanner.business.common.IAdHocTransactionService; +import org.navalplanner.business.common.IOnTransaction; +import org.navalplanner.business.common.IntegrationEntity; +import org.navalplanner.business.common.daos.IIntegrationEntityDAO; +import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.common.exceptions.ValidationException; +import org.navalplanner.ws.common.api.InstanceConstraintViolationsDTO; +import org.navalplanner.ws.common.api.InstanceConstraintViolationsListDTO; +import org.navalplanner.ws.common.api.IntegrationEntityDTO; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * This class provides generic support for implementing REST services + * as subclasses of this. . + * + * @author Fernando Bellas Permuy + */ +public abstract class GenericRESTService { + + @Autowired + private IAdHocTransactionService transactionService; + + /** + * It retrieves all entities. + */ + protected List findAll() { + return toDTO(getIntegrationEntityDAO().findAll()); + } + + /** + * It saves (inserts or updates) a list of entities. Each entity is + * saved in a separate transaction. + */ + protected InstanceConstraintViolationsListDTO save(List entityDTOs) { + + List instanceConstraintViolationsList = + new ArrayList(); + Long numItem = new Long(1); + + for (DTO entityDTO : entityDTOs) { + + InstanceConstraintViolationsDTO instanceConstraintViolationsDTO = + null; + + try { + insertOrUpdate(entityDTO); + } catch (ValidationException e) { + instanceConstraintViolationsDTO = + ConstraintViolationConverter.toDTO( + Util.generateInstanceConstraintViolationsDTOId( + numItem, entityDTO), e); + } catch (RuntimeException e) { + instanceConstraintViolationsDTO = + ConstraintViolationConverter.toDTO( + Util.generateInstanceConstraintViolationsDTOId( + numItem, entityDTO), e); + } + + if (instanceConstraintViolationsDTO != null) { + instanceConstraintViolationsList.add( + instanceConstraintViolationsDTO); + } + + numItem++; + + } + + return new InstanceConstraintViolationsListDTO( + instanceConstraintViolationsList); + + } + + /** + * It saves (inserts or updates) an entity DTO by using a new transaction. + * + * @throws ValidationException if validations are not passed + */ + protected void insertOrUpdate(final DTO entityDTO) + throws ValidationException { + + IOnTransaction save = new IOnTransaction() { + + @Override + public Void execute() { + + E entity = null; + IIntegrationEntityDAO entityDAO = + getIntegrationEntityDAO(); + + /* Insert or update? */ + try { + entity = entityDAO.findByCode(entityDTO.code); + updateEntity(entity, entityDTO); + } catch (InstanceNotFoundException e) { + entity = toEntity(entityDTO); + } + + /* + * Save the entity (insert or update). If validations are + * not passed (they are automatically executed by + * GenericDAO::save), the transaction is rolled back since + * ValidationException is a runtime exception, automatically + * discarding any change made to the entity. + * + */ + entityDAO.save(entity); + + return null; + + } + + }; + + transactionService.runOnAnotherTransaction(save); + + } + + /** + * It creates an entity from a DTO. + */ + protected abstract E toEntity(DTO entityDTO); + + /** + * It creates a DTO from an entity. + */ + protected abstract DTO toDTO(E entity); + + /** + * It must return the DAO for the entity "E". + */ + protected abstract IIntegrationEntityDAO + getIntegrationEntityDAO(); + + /** + * It must update the entity from the DTO. + * + * @throws ValidationException if updating is not possible + */ + protected abstract void updateEntity(E entity, DTO entityDTO) + throws ValidationException; + + /** + * It returns a list of DTOs from a list of entities. + */ + protected List toDTO(List entities) { + + List dtos = new ArrayList(); + + for (E entity : entities) { + dtos.add(toDTO(entity)); + } + + return dtos; + + } + +} diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/api/CriterionDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/api/CriterionDTO.java index 4a42e7d4c..f8b636dc0 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/api/CriterionDTO.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/api/CriterionDTO.java @@ -22,13 +22,11 @@ package org.navalplanner.ws.resources.criterion.api; import java.util.ArrayList; import java.util.List; -import java.util.Set; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; -import org.navalplanner.ws.common.api.DuplicateCodeBeingImportedException; import org.navalplanner.ws.common.api.IntegrationEntityDTO; /** @@ -79,16 +77,4 @@ public class CriterionDTO extends IntegrationEntityDTO { return ENTITY_TYPE; } - @Override - public void checkDuplicateCode(Set existingKeys) - throws DuplicateCodeBeingImportedException { - - super.checkDuplicateCode(existingKeys); - - for (CriterionDTO c : children) { - c.checkDuplicateCode(existingKeys); - } - - } - } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/api/CriterionTypeDTO.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/api/CriterionTypeDTO.java index 47d443b68..e5e8d8359 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/api/CriterionTypeDTO.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/api/CriterionTypeDTO.java @@ -22,13 +22,11 @@ package org.navalplanner.ws.resources.criterion.api; import java.util.ArrayList; import java.util.List; -import java.util.Set; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElementWrapper; -import org.navalplanner.ws.common.api.DuplicateCodeBeingImportedException; import org.navalplanner.ws.common.api.IntegrationEntityDTO; import org.navalplanner.ws.common.api.ResourceEnumDTO; @@ -103,21 +101,4 @@ public class CriterionTypeDTO extends IntegrationEntityDTO { return ENTITY_TYPE; } - @Override - public String[] getNaturalKeyValues() { - return new String[] {name}; - } - - @Override - public void checkDuplicateCode(Set existingKeys) - throws DuplicateCodeBeingImportedException { - - super.checkDuplicateCode(existingKeys); - - for (CriterionDTO c : criterions) { - c.checkDuplicateCode(existingKeys); - } - - } - } diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/impl/CriterionConverter.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/impl/CriterionConverter.java index cbae4338f..443e7dab2 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/impl/CriterionConverter.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/impl/CriterionConverter.java @@ -36,7 +36,6 @@ import org.navalplanner.business.resources.entities.CriterionType; import org.navalplanner.ws.common.impl.ResourceEnumConverter; import org.navalplanner.ws.resources.criterion.api.CriterionDTO; import org.navalplanner.ws.resources.criterion.api.CriterionTypeDTO; -import org.navalplanner.ws.resources.criterion.api.CriterionTypeListDTO; /** * Converter from/to criterion-related entities to/from DTOs. @@ -47,20 +46,6 @@ public final class CriterionConverter { private CriterionConverter() {} - public final static CriterionTypeListDTO toDTO( - Collection criterionTypes) { - - List criterionTypeDTOs = - new ArrayList(); - - for (CriterionType c : criterionTypes) { - criterionTypeDTOs.add(toDTO(c)); - } - - return new CriterionTypeListDTO(criterionTypeDTOs); - - } - public final static CriterionTypeDTO toDTO(CriterionType criterionType) { List criterionDTOs = new ArrayList(); diff --git a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/impl/CriterionServiceREST.java b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/impl/CriterionServiceREST.java index c74f196bd..a54b54263 100644 --- a/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/impl/CriterionServiceREST.java +++ b/navalplanner-webapp/src/main/java/org/navalplanner/ws/resources/criterion/impl/CriterionServiceREST.java @@ -20,30 +20,18 @@ package org.navalplanner.ws.resources.criterion.impl; -import static org.navalplanner.web.I18nHelper._; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import javax.ws.rs.Consumes; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; -import org.navalplanner.business.common.exceptions.InstanceNotFoundException; +import org.navalplanner.business.common.daos.IIntegrationEntityDAO; import org.navalplanner.business.common.exceptions.ValidationException; import org.navalplanner.business.resources.daos.ICriterionTypeDAO; import org.navalplanner.business.resources.entities.CriterionType; -import org.navalplanner.ws.common.api.DuplicateCodeBeingImportedException; -import org.navalplanner.ws.common.api.DuplicateNaturalKeyBeingImportedException; -import org.navalplanner.ws.common.api.InstanceConstraintViolationsDTO; import org.navalplanner.ws.common.api.InstanceConstraintViolationsListDTO; -import org.navalplanner.ws.common.impl.ConstraintViolationConverter; -import org.navalplanner.ws.common.impl.Util; +import org.navalplanner.ws.common.impl.GenericRESTService; import org.navalplanner.ws.resources.criterion.api.CriterionTypeDTO; import org.navalplanner.ws.resources.criterion.api.CriterionTypeListDTO; import org.navalplanner.ws.resources.criterion.api.ICriterionService; @@ -59,7 +47,9 @@ import org.springframework.transaction.annotation.Transactional; @Path("/criteriontypes/") @Produces("application/xml") @Service("criterionServiceREST") -public class CriterionServiceREST implements ICriterionService { +public class CriterionServiceREST + extends GenericRESTService + implements ICriterionService { @Autowired private ICriterionTypeDAO criterionTypeDAO; @@ -68,137 +58,39 @@ public class CriterionServiceREST implements ICriterionService { @GET @Transactional(readOnly = true) public CriterionTypeListDTO getCriterionTypes() { - return CriterionConverter.toDTO(criterionTypeDAO.getCriterionTypes()); + return new CriterionTypeListDTO(findAll()); } @Override @POST @Consumes("application/xml") - @Transactional public InstanceConstraintViolationsListDTO addCriterionTypes( CriterionTypeListDTO criterionTypes) { - List instanceConstraintViolationsList = - new ArrayList(); - Long numItem = new Long(1); - Set existingKeys = new HashSet(); - - /* Process criterion types. */ - for (CriterionTypeDTO criterionTypeDTO : - criterionTypes.criterionTypes) { - - InstanceConstraintViolationsDTO instanceConstraintViolationsDTO = - null; - CriterionType criterionType = null; - - try { - - /* - * We must detect if there exists another instance being - * imported with the same code or natural key, since - * "IntegrationEntity::checkConstraintUniqueCode" and - * the natural key unique constraint rule - * (@AssertTrue/@AssertFalse method) in the concrete entity only - * can check this condition with respect to the entities already - * existing in database (such methods use DAO - * "xxxAnotherTransaction" methods to avoid Hibernate to launch - * INSERT statements for new objects when launching queries in - * conversational use cases). - */ - criterionTypeDTO.checkDuplicateCode(existingKeys); - criterionTypeDTO.checkDuplicateNaturalKey(existingKeys); - - /* - * Convert DTO to entity. Note that the entity, if exists in - * the database, must be retrieved in another transaction. - * Otherwise (if retrieved as part of the current - * transaction), if the implementation of "updateEntity" makes - * modifications to the entity passed as a parameter and - * then throws an exception (because something make impossible - * to continue updating), the entity would be considered as - * dirty because it would be contained in the underlying ORM - * session, and in consequence, the ORM would try to update it - * when committing the transaction. Furthermore, the entity - * must be initialized so that "updateEntity" can access - * related entities. - */ - try { - criterionType = - findByCodeAnotherTransactionInitialized( - criterionTypeDTO.code); - udpateEntity(criterionType, criterionTypeDTO); - } catch (InstanceNotFoundException e) { - criterionType = toEntity(criterionTypeDTO); - } - - /* - * Save the entity (insert or update). - * - * "validate" is executed before "save", since "save" first - * adds the object to the underlying ORM session and then - * validates. So, if "validate" method is not called explicitly - * before "save", an invalid entity would be added to the - * underlying ORM session, causing the invalid entity to be - * added to the database when the ORM commits the transaction. - * As a side effect, validations are executed twice. - */ - criterionType.validate(); - criterionTypeDAO.save(criterionType); - - } catch (DuplicateCodeBeingImportedException e) { - instanceConstraintViolationsDTO = - InstanceConstraintViolationsDTO.create( - Util.generateInstanceConstraintViolationsDTOId(numItem, - criterionTypeDTO), - _("code: {0} is used by another instance of type {1} " + - "being imported", e.getCode(), e.getEntityType())); - } catch (DuplicateNaturalKeyBeingImportedException e) { - instanceConstraintViolationsDTO = - InstanceConstraintViolationsDTO.create( - Util.generateInstanceConstraintViolationsDTOId(numItem, - criterionTypeDTO), - _("values: {0} are used by another instance of type " + - "{1} being imported", - Arrays.toString(e.getNaturalKeyValues()), - e.getEntityType())); - } catch (ValidationException e) { - instanceConstraintViolationsDTO = - ConstraintViolationConverter.toDTO( - Util.generateInstanceConstraintViolationsDTOId( - numItem, criterionTypeDTO), e); - } - - - if (instanceConstraintViolationsDTO != null) { - instanceConstraintViolationsList.add( - instanceConstraintViolationsDTO); - } - - numItem++; - - } - - return new InstanceConstraintViolationsListDTO( - instanceConstraintViolationsList); + return save(criterionTypes.criterionTypes); } - private CriterionType findByCodeAnotherTransactionInitialized( - String code) throws InstanceNotFoundException { - - return criterionTypeDAO.findByCodeAnotherTransactionInitialized( - code); - + @Override + protected CriterionType toEntity(CriterionTypeDTO entityDTO) { + return CriterionConverter.toEntity(entityDTO); } - private CriterionType toEntity(CriterionTypeDTO criterionTypeDTO) { - return CriterionConverter.toEntity(criterionTypeDTO); + @Override + protected CriterionTypeDTO toDTO(CriterionType entity) { + return CriterionConverter.toDTO(entity); } - private void udpateEntity(CriterionType criterionType, - CriterionTypeDTO criterionTypeDTO) throws ValidationException { + @Override + protected IIntegrationEntityDAO getIntegrationEntityDAO() { + return criterionTypeDAO; + } - CriterionConverter.updateCriterionType(criterionType, criterionTypeDTO); + @Override + protected void updateEntity(CriterionType entity, + CriterionTypeDTO entityDTO) throws ValidationException { + + CriterionConverter.updateCriterionType(entity, entityDTO); } diff --git a/navalplanner-webapp/src/test/java/org/navalplanner/web/test/ws/resources/criterion/api/CriterionServiceTest.java b/navalplanner-webapp/src/test/java/org/navalplanner/web/test/ws/resources/criterion/api/CriterionServiceTest.java index a9020d606..4f6041350 100644 --- a/navalplanner-webapp/src/test/java/org/navalplanner/web/test/ws/resources/criterion/api/CriterionServiceTest.java +++ b/navalplanner-webapp/src/test/java/org/navalplanner/web/test/ws/resources/criterion/api/CriterionServiceTest.java @@ -28,7 +28,6 @@ import static org.navalplanner.business.BusinessGlobalNames.BUSINESS_SPRING_CONF import static org.navalplanner.web.WebappGlobalNames.WEBAPP_SPRING_CONFIG_FILE; import static org.navalplanner.web.test.WebappGlobalNames.WEBAPP_SPRING_CONFIG_TEST_FILE; import static org.navalplanner.web.test.ws.common.Util.assertNoConstraintViolations; -import static org.navalplanner.web.test.ws.common.Util.assertOneConstraintViolation; import static org.navalplanner.web.test.ws.common.Util.getUniqueName; import java.util.ArrayList; @@ -71,9 +70,10 @@ public class CriterionServiceTest { private ICriterionTypeDAO criterionTypeDAO; @Test + @NotTransactional public void testAddAndGetCriterionTypes() { - /* Build criterion type "ct1" (4 constraint violations). */ + /* Build criterion type "ct1" (5 constraint violations). */ CriterionDTO ct1c1 = new CriterionDTO(null, true, // Missing criterion // name. new ArrayList()); @@ -91,11 +91,15 @@ public class CriterionServiceTest { new ArrayList()); CriterionDTO ct1c4 = new CriterionDTO(" C3 ", true, new ArrayList()); // Repeated criterion name. + CriterionDTO ct1c5 = new CriterionDTO(ct1c3.code, // Repeated criterion + "c4", true, // code inside this criterion type. + new ArrayList()); List ct1Criterions = new ArrayList(); ct1Criterions.add(ct1c1); ct1Criterions.add(ct1c2); ct1Criterions.add(ct1c3); ct1Criterions.add(ct1c4); + ct1Criterions.add(ct1c5); String ct1Name = null; CriterionTypeDTO ct1 = new CriterionTypeDTO(ct1Name, "desc", false, true, true, ResourceEnumDTO.RESOURCE, // Missing criterion @@ -141,9 +145,18 @@ public class CriterionServiceTest { "desc", true, true, true, ResourceEnumDTO.RESOURCE, new ArrayList()); + /* Build criterion type "ct5" (1 constraint violation). */ + CriterionDTO ct5c1 = new CriterionDTO(ct3c1.code, // Criterion code + "c1", true, // used by another criterion type. + new ArrayList()); + List ct5Criterions = new ArrayList(); + ct5Criterions.add(ct5c1); + CriterionTypeDTO ct5 = new CriterionTypeDTO(getUniqueName(), "desc", + true, true, true, ResourceEnumDTO.RESOURCE, ct5Criterions); + /* Criterion type list. */ CriterionTypeListDTO criterionTypes = - createCriterionTypeListDTO(ct1, ct2, ct3, ct4); + createCriterionTypeListDTO(ct1, ct2, ct3, ct4, ct5); List instanceConstraintViolationsList = criterionService.addCriterionTypes(criterionTypes). @@ -151,12 +164,12 @@ public class CriterionServiceTest { assertTrue( instanceConstraintViolationsList.toString(), - instanceConstraintViolationsList.size() == 3); + instanceConstraintViolationsList.size() == 4); assertTrue( instanceConstraintViolationsList.get(0). constraintViolations.toString(), instanceConstraintViolationsList.get(0). - constraintViolations.size() == 4); + constraintViolations.size() == 5); assertTrue( instanceConstraintViolationsList.get(1). constraintViolations.toString(), @@ -167,6 +180,11 @@ public class CriterionServiceTest { constraintViolations.toString(), instanceConstraintViolationsList.get(2). constraintViolations.size() == 1); + assertTrue( + instanceConstraintViolationsList.get(3). + constraintViolations.toString(), + instanceConstraintViolationsList.get(3). + constraintViolations.size() == 1); /* Find criterion types. */ List returnedCriterionTypes = @@ -179,28 +197,7 @@ public class CriterionServiceTest { } @Test - @NotTransactional - public void testAddRepeatedCriterionTypeThatAlreadyExistsInDB() - throws InstanceNotFoundException { - - CriterionTypeDTO criterionType = new CriterionTypeDTO( - getUniqueName(), "desc", true, true, true, - ResourceEnumDTO.RESOURCE, new ArrayList()); - - assertNoConstraintViolations(criterionService.addCriterionTypes( - createCriterionTypeListDTO(criterionType))); - - criterionType.code = getUniqueName(); // Another criterion with the - // same data. - assertOneConstraintViolation(criterionService.addCriterionTypes( - createCriterionTypeListDTO(criterionType))); // Repeated criterion - // type name. - - } - - - @Test - @NotTransactional + @Transactional public void testUpdateCriterionType() throws InstanceNotFoundException { /* Build criterion type with criteria: c1, c2->c2-1. */ @@ -245,8 +242,7 @@ public class CriterionServiceTest { assertNoConstraintViolations(criterionService.addCriterionTypes( createCriterionTypeListDTO(ctUpdated))); - CriterionType ctEntity = - criterionTypeDAO.findByCodeAnotherTransactionInitialized(ct.code); + CriterionType ctEntity = criterionTypeDAO.findByCode(ct.code); assertTrue(ctEntity.getCriterions().size() == 4); /* Test criterion hierarchy. */ diff --git a/scripts/rest-clients/criterion-types-sample.xml b/scripts/rest-clients/criterion-types-sample.xml index 007dbdc16..79042977a 100644 --- a/scripts/rest-clients/criterion-types-sample.xml +++ b/scripts/rest-clients/criterion-types-sample.xml @@ -37,6 +37,12 @@ + + + - - - - - - - - + - @@ -108,37 +101,26 @@ - - - - + - - + + - + - - - - - - - + diff --git a/scripts/rest-clients/criterion-types-update-sample.xml b/scripts/rest-clients/criterion-types-update-sample.xml index ae0fbb1ad..957543d4d 100644 --- a/scripts/rest-clients/criterion-types-update-sample.xml +++ b/scripts/rest-clients/criterion-types-update-sample.xml @@ -1,17 +1,23 @@ + + - - - - - + + +