From 146cac3b56c2f851a5ddb9bbbb8e730844703cb4 Mon Sep 17 00:00:00 2001 From: rkv Date: Fri, 21 Dec 2012 09:01:38 +0000 Subject: [PATCH] Fix: document removing is fixed. Unit tests for StudyDAO and StepService.removeDocument() are created. --- .../src/org/splat/dal/bo/kernel/Entity.java | 222 ++-- .../splat/dal/bo/kernel/Persistent.hbm.xml | 5 + .../org/splat/dal/bo/kernel/Relation.hbm.xml | 3 + .../src/org/splat/dal/bo/kernel/Relation.java | 178 ++-- .../org/splat/dal/bo/som/ActorRelation.java | 5 +- .../splat/dal/bo/som/ConvertsRelation.java | 19 +- .../org/splat/dal/bo/som/DocumentType.java | 4 +- .../org/splat/dal/bo/som/Publication.hbm.xml | 2 +- .../org/splat/dal/bo/som/Relations.hbm.xml | 14 +- .../src/org/splat/dal/bo/som/Scenario.hbm.xml | 2 +- .../org/splat/dal/bo/som/StampRelation.java | 8 +- .../src/org/splat/dal/bo/som/Study.hbm.xml | 2 +- .../org/splat/dal/bo/som/UsedByRelation.java | 16 +- .../org/splat/dal/bo/som/UsesRelation.java | 16 +- .../dal/bo/som/ValidationCycleRelation.java | 8 +- .../splat/dal/bo/som/VersionsRelation.java | 18 +- .../src/org/splat/service/StepService.java | 8 +- .../org/splat/service/StepServiceImpl.java | 116 ++- .../src/spring/businessServiceContext.xml | 6 +- .../src/test/splat/dao/TestStudyDAO.java | 728 +++++++++++++ .../test/splat/service/TestStepService.java | 548 ++++++++++ Workspace/Siman-Common/testng-stepService.xml | 8 + .../WEB-INF/lib/jsonplugin-0.34.jar | Bin 0 -> 47456 bytes .../Siman/WebContent/jap/splat-launcher.jar | Bin 10713 -> 10714 bytes .../WebContent/jap/splat-signedlauncher.jar | Bin 12434 -> 12436 bytes Workspace/Siman/WebContent/jsp/menuitem.jsp | 4 + .../Siman/WebContent/layout/homeLayout.jsp | 20 +- .../study/editScenarioProperties.jsp | 13 - Workspace/Siman/src/hibernate.properties | 2 +- Workspace/Siman/src/jdbc.properties | 2 +- .../Siman/src/org/splat/simer/Action.java | 27 +- .../org/splat/simer/ApplicationSettings.java | 188 ++-- .../src/org/splat/simer/ConnectionAction.java | 2 +- .../Siman/src/org/splat/simer/Converter.java | 173 ++-- .../org/splat/simer/EditDocumentAction.java | 17 +- .../src/org/splat/simer/EditStudyAction.java | 34 +- .../src/org/splat/simer/ExceptionAction.java | 217 ++++ .../splat/simer/StudyPropertiesAction.java | 3 +- .../Siman/src/spring/applicationContext.xml | 183 ++-- Workspace/Siman/src/struts.xml | 960 +++++++++++------- 40 files changed, 2921 insertions(+), 860 deletions(-) create mode 100644 Workspace/Siman-Common/src/test/splat/dao/TestStudyDAO.java create mode 100644 Workspace/Siman-Common/src/test/splat/service/TestStepService.java create mode 100644 Workspace/Siman-Common/testng-stepService.xml create mode 100644 Workspace/Siman/WebContent/WEB-INF/lib/jsonplugin-0.34.jar create mode 100644 Workspace/Siman/src/org/splat/simer/ExceptionAction.java diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Entity.java b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Entity.java index c66a7ce..bb8f0d4 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Entity.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Entity.java @@ -1,4 +1,5 @@ package org.splat.dal.bo.kernel; + /** * Abstract root class of persistent objects supporting relations to other persistent objects.
* Relations are instances of concrete subclasses of Relation that are assigned to Entity objects at run time.
@@ -10,93 +11,166 @@ package org.splat.dal.bo.kernel; * @copyright OPEN CASCADE 2012 */ +import java.util.ArrayList; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Set; -import java.util.Vector; import org.splat.kernel.InvalidPropertyException; import org.splat.kernel.MissedPropertyException; import org.splat.kernel.MultiplyDefinedException; import org.splat.kernel.ObjectProperties; +import org.splat.log.AppLogger; - +/** + * Persistent entity class. Entity can have relations to other persistent objects. + */ public abstract class Entity extends Any { - private final Set relations = new HashSet(); - -// ============================================================================================================================== -// Constructors -// ============================================================================================================================== - -// Database fetch constructor - protected Entity () { - } -// Initialization constructor - protected Entity (final ObjectProperties prop) throws MissedPropertyException, InvalidPropertyException, MultiplyDefinedException { - super(prop); - } - -// ============================================================================================================================== -// Public member functions -// ============================================================================================================================== - - public Relation getFirstRelation (final Class type) { -// ----------------------------------------------------------------- - for (Iterator i=relations.iterator(); i.hasNext();) { - Relation link = i.next(); - if (link.getClass().equals(type)) { - return link; + /** + * The service logger. + */ + public final static AppLogger LOG = AppLogger.getLogger(Entity.class); + + /** + * Persistent collection of relations to other persistent objects. + */ + private final Set relations = new HashSet(); // NOPMD:RKV: mapped as field + + /** + * Database fetch constructor. + */ + protected Entity() { + super(); + } + + /** + * Initialization constructor. + * + * @param prop + * object properties + * @throws MissedPropertyException + * if some mandatory property is missed + * @throws InvalidPropertyException + * if some property has invalid value + * @throws MultiplyDefinedException + * if some property is defined several times + */ + protected Entity(final ObjectProperties prop) + throws MissedPropertyException, InvalidPropertyException, + MultiplyDefinedException { + super(prop); + } + + // ============================================================================================================================== + // Public member functions + // ============================================================================================================================== + + /** + * Get first relation of the given type. + * + * @param type + * the relation type to look for + * @return the found relation or null + */ + public Relation getFirstRelation(final Class type) { + Relation found = null; + for (Relation link : relations) { + if (link.getClass().equals(type)) { + found = link; + } } - } - return null; - } - - public List getRelations (final Class type) { - List result = new Vector(); - - for (Iterator i=relations.iterator(); i.hasNext();) { - Relation link = i.next(); - if (link.getClass().equals(type)) { - result.add(link); + return found; + } + + /** + * Get list of all relations of the given type. + * + * @param type + * the relation type to look for + * @return the list if found relations + */ + public List getRelations(final Class type) { + List result = new ArrayList(); + + for (Relation link : relations) { + if (link.getClass().equals(type)) { + result.add(link); + } } - } - return result; - } - -// ============================================================================================================================== -// Protected services -// ============================================================================================================================== - - public Set getAllRelations () { - return relations; - } - - public Relation addRelation (Relation link) { - - relations.add(link); - - if (link.isBidirectional()) { - Entity to = (Entity)link.getTo(); // Bidirectional relation are necessarily between entities - - link = link.getReverse(); - to.relations.add(link); - } - return link; - } - - public void removeRelation (final Class type, final Persistent to) { - for (Iterator i=relations.iterator(); i.hasNext();) { - Relation link = i.next(); - if (!link.getClass().equals(type)) { - continue; + return result; + } + + // ============================================================================================================================== + // Protected services + // ============================================================================================================================== + + /** + * Get persistent collection of relations. + * + * @return entity relations set + */ + public Set getAllRelations() { + return relations; + } + + /** + * Add a relation for the entity. If the link is bidirectional then also add the reverse link to the referenced object. + * + * @param link + * the relation from this entity to some persistent object + * @return the reverse link if the given link is bidirectional otherwise returns the given link + */ + public Relation addRelation(final Relation link) { + + Relation back = link; + relations.add(link); + + if (link.isBidirectional()) { + Entity to = (Entity) link.getTo(); // Bidirectional relation are necessarily between entities + + back = link.getReverse(); + to.relations.add(back); + } + return back; + } + + /** + * Remove the first relation of the given type to the given object. + * + * @param type + * the type of the relation to remove + * @param to + * the referenced object to identify the relation to remove + * @return the removed relation + */ + public Relation removeRelation(final Class type, + final Persistent to) { + Relation res = null; + if (LOG.isDebugEnabled()) { + LOG.debug("Try to remove the link " + type.getSimpleName() + + " from " + this.toString() + " to " + to.toString()); + } + for (Relation link : relations) { + if (LOG.isDebugEnabled()) { + LOG.debug("Compare the link " + link.toString()); + LOG.debug("link.getClass().equals(type): " + + link.getClass().equals(type)); + } + if (link.getClass().equals(type) && link.getTo().equals(to)) { + if (LOG.isDebugEnabled()) { + LOG.debug("Remove the link " + + link.getClass().getSimpleName() + " from " + + this.toString() + " to " + to.toString()); + } + res = link; + break; + } } - if (!link.getTo().equals(to)) { - continue; + this.getAllRelations().remove(res); + if (res.isBidirectional()) { + ((Entity)res.getTo()).getAllRelations().remove(res.getReverse()); } - i.remove(); - return; - } - } + return res; + } } \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Persistent.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Persistent.hbm.xml index a4a9ef9..bd34d98 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Persistent.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Persistent.hbm.xml @@ -16,5 +16,10 @@ 1000 + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.hbm.xml index 2ffa7f5..b8a52df 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.hbm.xml @@ -15,5 +15,8 @@ + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.java index 1ee3833..c5bc21e 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/kernel/Relation.java @@ -1,10 +1,10 @@ package org.splat.dal.bo.kernel; + + /** - * Base implementation of relations between entities.
- * A relation makes a typed link from an entity to any kind persistent object. The relations are typed by subclasses of this - * abstract class which define the actual object referenced by the relation.
- * This Relation base class implements unidirectional relations. The bidirectionality must be implemented in concrete subclasses - * by: + * Base implementation of relations between entities.
A relation makes a typed link from an entity to any kind persistent object. The + * relations are typed by subclasses of this abstract class which define the actual object referenced by the relation.
This Relation + * base class implements unidirectional relations. The bidirectionality must be implemented in concrete subclasses by: *
    *
  • overriding the isBidirectional() and getReverseClass() methods,
  • *
  • creating the reverse relation in constructors.
  • @@ -12,95 +12,89 @@ package org.splat.dal.bo.kernel; * Relation objects also support dynamic attributes provided by the Any class. * * @see Entity - * @author Daniel Brunier-Coulin + * @author Daniel Brunier-Coulin * @copyright OPEN CASCADE 2012 */ - -import java.util.Iterator; - public abstract class Relation extends Any { -// Persistent fields - protected Entity owner; // Study or Document - -// Transient field - protected Relation reverse; - -// ============================================================================================================================== -// Constructors -// ============================================================================================================================== - -// Database fetch constructor - protected Relation () { -// --------------------- - reverse = null; - } -// Initialization constructor - protected Relation (Entity from) { -// -------------------------------- - this.owner = from; - this.reverse = null; // Initialized by subclasses - } - -// ============================================================================================================================== -// Public member functions -// ============================================================================================================================== - - public Entity getFrom () { -// ------------------------ - return owner; - } - - public Relation getReverse () { -// ----------------------------- - if (!this.isBidirectional() || reverse != null) return reverse; - - Class type = this.getReverseClass(); - Entity to = (Entity)this.getTo(); // Bidirectional relations are necessarily between Entities - - for (Iterator i=to.getAllRelations().iterator(); i.hasNext(); ) { - Relation asked = i.next(); - if (!asked.getClass().equals(type)) continue; - if (!asked.getTo().equals(owner)) continue; - reverse = asked; - reverse.reverse = this; // For benefiting from this execution - return reverse; - } - return null; - } - - public Class getReverseClass () { -// --------------------------------------------------- - return null; - } - - public boolean isBidirectional () { -// --------------------------------- - return false; - } - -/** - * Moves this relation from its current owner entity to the given one. - * - * @param nowner the document to which this relation is moved - * */ - public void moveTo (Entity nowner) { - - Entity oldOwner = this.owner; - this.owner = nowner; - nowner.getAllRelations().add(this); - oldOwner.getAllRelations().remove(this); - - if (this.isBidirectional()) { - Relation link = this.getReverse(); - link.setTo(nowner); - } - } - -// ============================================================================================================================== -// Abstract functions -// ============================================================================================================================== - - public abstract Persistent getTo (); - protected abstract void setTo (Persistent to); // For the need of the moveTo() method + // Persistent fields + protected Entity owner; // Study or Document + + // Transient field + protected Relation reverse; + + // ============================================================================================================================== + // Constructors + // ============================================================================================================================== + + // Database fetch constructor + protected Relation() { + reverse = null; + } + + // Initialization constructor + protected Relation(final Entity from) { + this.owner = from; + this.reverse = null; // Initialized by subclasses + } + + // ============================================================================================================================== + // Public member functions + // ============================================================================================================================== + + public Entity getFrom() { + return owner; + } + + public Relation getReverse() { + if (this.isBidirectional() && reverse == null) { + Class type = this.getReverseClass(); + Entity to = (Entity) this.getTo(); // Bidirectional relations are necessarily between Entities + + for (Relation asked : to.getAllRelations()) { + if (asked.getClass().equals(type) + && asked.getTo().equals(owner)) { + reverse = asked; + reverse.reverse = this; // For benefiting from this execution + break; + } + } + } + return reverse; + } + + public Class getReverseClass() { + return null; + } + + public boolean isBidirectional() { + return false; + } + + /** + * Moves this relation from its current owner entity to the given one. + * + * @param nowner + * the document to which this relation is moved + */ + public void moveTo(final Entity nowner) { + + Entity oldOwner = this.owner; + this.owner = nowner; + nowner.getAllRelations().add(this); + oldOwner.getAllRelations().remove(this); + + if (this.isBidirectional()) { + Relation link = this.getReverse(); + link.setTo(nowner); + } + } + + // ============================================================================================================================== + // Abstract functions + // ============================================================================================================================== + + public abstract Persistent getTo(); + + public abstract void setTo(Persistent to); // For the need of the moveTo() method } \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/ActorRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ActorRelation.java index 98fdf8a..8d46394 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/som/ActorRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ActorRelation.java @@ -31,7 +31,7 @@ public abstract class ActorRelation extends Relation { protected ActorRelation () { } // ActorRelation subclasses constructor - protected ActorRelation (Study from, User to) { + protected ActorRelation (final Study from, final User to) { // --------------------------------------------- super(from); this.refer = to; @@ -46,7 +46,8 @@ public abstract class ActorRelation extends Relation { // -------------------- return refer; } - protected void setTo (Persistent to) { + @Override + public void setTo (final Persistent to) { // ------------------------------------ refer = (User)to; } diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/ConvertsRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ConvertsRelation.java index 1e79f75..b23f650 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/som/ConvertsRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ConvertsRelation.java @@ -29,20 +29,22 @@ public class ConvertsRelation extends Relation { description = null; } // Initialization constructors - protected ConvertsRelation (Document from, File to) { + protected ConvertsRelation (final Document from, final File to) { // --------------------------------------------------- super(from); this.refer = to; this.got = true; this.description = null; // Conversion not described } - public ConvertsRelation (Document from, File to, String description) { + public ConvertsRelation (final Document from, final File to, final String description) { // ----------------------------------------------------------------------- super(from); this.refer = to; this.got = true; this.description = description; // May be null - if (description != null) this.setAttribute( new DescriptionAttribute(this, description) ); + if (description != null) { + this.setAttribute( new DescriptionAttribute(this, description) ); + } } // ============================================================================================================================== @@ -53,17 +55,22 @@ public class ConvertsRelation extends Relation { // ------------------------------- if (!got) { DescriptionAttribute field = (DescriptionAttribute)this.getAttribute(DescriptionAttribute.class); - if (field != null) description = field.getValue(); + if (field != null) { + description = field.getValue(); + } got = true; // Don't need to be modified later as set and remove attribute functions are private to this class } return description; // May be null } - public File getTo () { + @Override + public File getTo () { // -------------------- return refer; } - protected void setTo (Persistent to) { + + @Override + public void setTo (final Persistent to) { // ------------------------------------ refer = (File)to; } diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/DocumentType.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/DocumentType.java index 4e285ec..d86176c 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/som/DocumentType.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/DocumentType.java @@ -302,6 +302,8 @@ public class DocumentType extends Persistent { this.step = tprop.step; this.result = tprop.result; this.uses.clear(); - this.uses.addAll(Arrays.asList(tprop.uses)); + if (tprop.uses != null) { + this.uses.addAll(Arrays.asList(tprop.uses)); + } } } \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/Publication.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Publication.hbm.xml index d5e6f08..c5a26d5 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/som/Publication.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Publication.hbm.xml @@ -13,7 +13,7 @@ - + diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/Relations.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Relations.hbm.xml index 6ec782e..5dd0fb7 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/som/Relations.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Relations.hbm.xml @@ -13,45 +13,45 @@ - + - + - + - + - + - + - + \ No newline at end of file diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/Scenario.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Scenario.hbm.xml index efad6f2..fb54359 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/som/Scenario.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Scenario.hbm.xml @@ -11,7 +11,7 @@ - + diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/StampRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/StampRelation.java index ef4cb1b..2b6162d 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/som/StampRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/StampRelation.java @@ -21,7 +21,7 @@ public class StampRelation extends Relation { protected StampRelation () { } // Internal constructor - protected StampRelation (Document from, Timestamp to) { + protected StampRelation (final Document from, final Timestamp to) { // ----------------------------------------------------- super(from); this.refer = to; @@ -31,7 +31,8 @@ public class StampRelation extends Relation { // Public member functions // ============================================================================================================================== - public Timestamp getTo () { + @Override + public Timestamp getTo () { // ------------------------- return refer; } @@ -41,7 +42,8 @@ public class StampRelation extends Relation { return refer.getType(); } - protected void setTo (Persistent to) { + @Override + public void setTo (final Persistent to) { // ------------------------------------ refer = (Timestamp)to; } diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/Study.hbm.xml b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Study.hbm.xml index 1707b8e..43d7a93 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/som/Study.hbm.xml +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/Study.hbm.xml @@ -31,7 +31,7 @@ - + diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/UsedByRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/UsedByRelation.java index b5dedd1..5602fc9 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/som/UsedByRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/UsedByRelation.java @@ -21,14 +21,14 @@ public class UsedByRelation extends Relation { protected UsedByRelation () { } // Initialization constructors - protected UsedByRelation (Document from, Document to) { + protected UsedByRelation (final Document from, final Document to) { // ----------------------------------------------------- super(from); this.refer = to; this.reverse = new UsesRelation(this, to, from); } // Internal constructor - protected UsedByRelation (Relation back, Document from, Document to) { + protected UsedByRelation (final Relation back, final Document from, final Document to) { // -------------------------------------------------------------------- super(from); this.refer = to; @@ -39,19 +39,23 @@ public class UsedByRelation extends Relation { // Public member functions // ============================================================================================================================== - public Class getReverseClass () { + @Override + public Class getReverseClass () { // --------------------------------------------------- return UsesRelation.class; } - public Document getTo () { + @Override + public Document getTo () { // ------------------------- return refer; } - public boolean isBidirectional () { + @Override + public boolean isBidirectional () { // --------------------------------- return true; } - protected void setTo (Persistent to) { + @Override + public void setTo (final Persistent to) { // ------------------------------------ refer = (Document)to; } diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/UsesRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/UsesRelation.java index e153992..2a71351 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/som/UsesRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/UsesRelation.java @@ -21,14 +21,14 @@ public class UsesRelation extends Relation { protected UsesRelation () { } // Initialization constructors - public UsesRelation (Document from, Document to) { + public UsesRelation (final Document from, final Document to) { // --------------------------------------------------- super(from); this.refer = to; this.reverse = new UsedByRelation(this, to, from); } // Internal constructor - protected UsesRelation (Relation back, Document from, Document to) { + protected UsesRelation (final Relation back, final Document from, final Document to) { // ------------------------------------------------------------------ super(from); this.refer = to; @@ -39,20 +39,24 @@ public class UsesRelation extends Relation { // Public member functions // ============================================================================================================================== - public Class getReverseClass () { + @Override + public Class getReverseClass () { // --------------------------------------------------- return UsedByRelation.class; } - public Document getTo () { + @Override + public Document getTo () { // ------------------------- return refer; } - public boolean isBidirectional () { + @Override + public boolean isBidirectional () { // --------------------------------- return true; } - protected void setTo (Persistent to) { + @Override + public void setTo (final Persistent to) { // ------------------------------------ refer = (Document)to; } diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycleRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycleRelation.java index e3c4f15..007833d 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycleRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/ValidationCycleRelation.java @@ -21,7 +21,7 @@ public class ValidationCycleRelation extends Relation { protected ValidationCycleRelation () { } // Internal constructor - protected ValidationCycleRelation (Study from, ValidationCycle to) { + protected ValidationCycleRelation (final Study from, final ValidationCycle to) { // ------------------------------------------------------------------ super(from); this.refer = to; @@ -42,12 +42,14 @@ public class ValidationCycleRelation extends Relation { return refer.getDocumentType(); } - public ValidationCycle getTo () { + @Override + public ValidationCycle getTo () { // ------------------------------- return refer; } - protected void setTo (Persistent to) { + @Override + public void setTo (final Persistent to) { // ------------------------------------ refer = (ValidationCycle)to; } diff --git a/Workspace/Siman-Common/src/org/splat/dal/bo/som/VersionsRelation.java b/Workspace/Siman-Common/src/org/splat/dal/bo/som/VersionsRelation.java index c4e92cf..b906cdf 100644 --- a/Workspace/Siman-Common/src/org/splat/dal/bo/som/VersionsRelation.java +++ b/Workspace/Siman-Common/src/org/splat/dal/bo/som/VersionsRelation.java @@ -29,20 +29,22 @@ public class VersionsRelation extends Relation { description = null; } // Initialization constructors - public VersionsRelation (Document from, Document to) { + public VersionsRelation (final Document from, final Document to) { // ------------------------------------------------------- super(from); this.refer = to; this.got = true; this.description = null; // Conversion not described } - public VersionsRelation (Document from, Document to, String description) { + public VersionsRelation (final Document from, final Document to, final String description) { // --------------------------------------------------------------------------- super(from); this.refer = to; this.got = true; this.description = description; // May be null - if (description != null) this.setAttribute( new DescriptionAttribute(this, description) ); + if (description != null) { + this.setAttribute( new DescriptionAttribute(this, description) ); + } } // ============================================================================================================================== @@ -53,17 +55,21 @@ public class VersionsRelation extends Relation { // ------------------------------- if (!got) { DescriptionAttribute field = (DescriptionAttribute)this.getAttribute(DescriptionAttribute.class); - if (field != null) description = field.getValue(); + if (field != null) { + description = field.getValue(); + } got = true; // Don't need to be modified later as set and remove attribute functions are private to this class } return description; // May be null } - public Document getTo () { + @Override + public Document getTo () { // ------------------------- return refer; } - protected void setTo (Persistent to) { + @Override + public void setTo (final Persistent to) { // ------------------------------------ refer = (Document)to; } diff --git a/Workspace/Siman-Common/src/org/splat/service/StepService.java b/Workspace/Siman-Common/src/org/splat/service/StepService.java index bc93b46..95c1ccf 100644 --- a/Workspace/Siman-Common/src/org/splat/service/StepService.java +++ b/Workspace/Siman-Common/src/org/splat/service/StepService.java @@ -161,15 +161,15 @@ public interface StepService { boolean remove(Step aStep, Publication oldoc); /** - * Remove a document from the given step. + * Remove a document from the given step and from the database if it is no more used. * * @param aStep * the study step - * @param doctag - * the document publication + * @param docId + * the document id * @return true if removing of the document succeeded */ - boolean removeDocument(Step aStep, Publication doctag); + boolean removeDocument(Step aStep, long docId); /** * Get document types which are applicable for the given study step (activity). diff --git a/Workspace/Siman-Common/src/org/splat/service/StepServiceImpl.java b/Workspace/Siman-Common/src/org/splat/service/StepServiceImpl.java index 4aae91f..c0e9e1f 100644 --- a/Workspace/Siman-Common/src/org/splat/service/StepServiceImpl.java +++ b/Workspace/Siman-Common/src/org/splat/service/StepServiceImpl.java @@ -13,7 +13,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.Set; import org.splat.dal.bo.kernel.Relation; import org.splat.dal.bo.som.ConvertsRelation; @@ -28,9 +27,11 @@ import org.splat.dal.bo.som.SimulationContext; import org.splat.dal.bo.som.UsedByRelation; import org.splat.dal.bo.som.UsesRelation; import org.splat.dal.bo.som.VersionsRelation; +import org.splat.dal.dao.kernel.RelationDAO; import org.splat.dal.dao.som.DocumentDAO; import org.splat.dal.dao.som.FileDAO; import org.splat.dal.dao.som.ProjectElementDAO; +import org.splat.dal.dao.som.PublicationDAO; import org.splat.dal.dao.som.SimulationContextDAO; import org.splat.dal.dao.som.VersionsRelationDAO; import org.splat.kernel.InvalidPropertyException; @@ -73,6 +74,10 @@ public class StepServiceImpl implements StepService { * Injected document DAO. */ private DocumentDAO _documentDAO; + /** + * Injected relation DAO. + */ + private RelationDAO _relationDAO; /** * Injected file DAO. */ @@ -97,6 +102,10 @@ public class StepServiceImpl implements StepService { * Injected project service. */ private ProjectSettingsService _projectSettings; + /** + * Injected publication DAO. + */ + private PublicationDAO _publicationDAO; /** * {@inheritDoc} @@ -369,7 +378,7 @@ public class StepServiceImpl implements StepService { aStep.getStep(), newFormat))) { dprop.setFormat(newFormat); } - + if (dprop.getAuthor() == null) { dprop.setAuthor(previous.getAuthor()); } @@ -440,53 +449,68 @@ public class StepServiceImpl implements StepService { * @return true if removing of the publication succeeded */ public boolean remove(final Step aStep, final Publication oldoc) { - boolean res = aStep.getOwner().remove(oldoc); // Updates the study in memory - if (res) { - aStep.getDocuments().remove(oldoc); // Updates this step - ProjectElement owner = aStep.getOwner(); - if (owner != null) { - owner.remove(oldoc); - } - getDocumentService().release(oldoc.value()); // Decrements the configuration tag count of document - // The publication becoming orphan, it should automatically be removed from the database when updating of owner scenario. + aStep.getDocuments().remove(oldoc); // Updates this step + // Synchronize with database + ProjectElement owner = getProjectElementDAO().merge(aStep.getOwner()); + if (owner != null) { + aStep.getOwner().remove(oldoc); // in transient copy + owner.remove(oldoc); // in persistent copy } - return res; + getDocumentService().release(oldoc.value()); // Decrements the configuration tag count of document + // The publication becoming orphan, it should automatically be removed from the database when updating of owner scenario. + return true; } /** - * Remove a document from the given step. + * Remove a document from the given step and from the database if it is no more used. * * @param aStep * the study step - * @param doctag - * the document publication + * @param docId + * the document id * @return true if removing of the document succeeded */ @Transactional - public boolean removeDocument(final Step aStep, final Publication doctag) { - Document value = doctag.value(); - getDocumentDAO().update(value); - Publication torem = aStep.getDocument(value.getIndex()); - + public boolean removeDocument(final Step aStep, final long docId) { + Publication torem = aStep.getDocument(docId); boolean res = (torem != null); if (res) { + torem = getPublicationDAO().merge(torem); remove(aStep, torem); - getProjectElementDAO().update(aStep.getOwner()); + Document value = torem.value(); if (!value.isPublished() && !value.isVersioned()) { // The referenced document is no more used - Set links = value.getAllRelations(); // Get all relation of the document to remove them List using = new ArrayList(); - for (Iterator i = links.iterator(); i.hasNext();) { - Relation link = i.next(); + List converts = new ArrayList(); + for (Relation link : value.getAllRelations()) { if (link.getClass().equals(ConvertsRelation.class)) { // File conversion - getFileDAO().delete((File) link.getTo()); // The corresponding physical file is not removed from the vault + converts.add(link); } else if (link.getClass().equals(UsesRelation.class)) { // Document dependency using.add((Document) link.getTo()); } } - for (Iterator i = using.iterator(); i.hasNext();) { - i.next().removeRelation(UsedByRelation.class, value); // TODO: RKV: don't use Database.getSession in removeRelation +// value.getAllRelations().removeAll(converts); + // Remove relations from depending documents + if (LOG.isDebugEnabled()) { + LOG.debug("Remove " + using.size() + " UsedByRelation(s)."); + } + for (Document doc : using) { + if (LOG.isDebugEnabled()) { + LOG.debug("Remove UsedByRelation from " + + doc.getTitle() + " to " + value.getTitle()); + } + doc.removeRelation(UsedByRelation.class, value); } + // Synchronize deleted objects with the database to avoid hibernate exception + // related with null value of not-nullable property because back reference from Document to Publication is not mapped: + // org.hibernate.PropertyValueException: not-null property references a null or transient value: + // org.splat.dal.bo.som.Publication.mydoc + getDocumentDAO().flush(); // To show to the database that files from ConvertsRelation(s) are deleted already getDocumentDAO().delete(value); // The corresponding physical file is not removed from the vault + // Delete document's files + for (Relation link : converts) { + File file = (File) link.getTo(); + getFileDAO().delete(file); // The corresponding physical file is not removed from the vault + } } } return res; @@ -686,4 +710,42 @@ public class StepServiceImpl implements StepService { final ProjectSettingsService projectSettingsService) { _projectSettings = projectSettingsService; } + + /** + * Get the publicationDAO. + * + * @return the publicationDAO + */ + public PublicationDAO getPublicationDAO() { + return _publicationDAO; + } + + /** + * Set the publicationDAO. + * + * @param publicationDAO + * the publicationDAO to set + */ + public void setPublicationDAO(final PublicationDAO publicationDAO) { + this._publicationDAO = publicationDAO; + } + + /** + * Get the relationDAO. + * + * @return the relationDAO + */ + public RelationDAO getRelationDAO() { + return _relationDAO; + } + + /** + * Set the relationDAO. + * + * @param relationDAO + * the relationDAO to set + */ + public void setRelationDAO(final RelationDAO relationDAO) { + _relationDAO = relationDAO; + } } diff --git a/Workspace/Siman-Common/src/spring/businessServiceContext.xml b/Workspace/Siman-Common/src/spring/businessServiceContext.xml index 201c9e5..e8cbab3 100644 --- a/Workspace/Siman-Common/src/spring/businessServiceContext.xml +++ b/Workspace/Siman-Common/src/spring/businessServiceContext.xml @@ -107,8 +107,10 @@ http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> - - + + + + Roman Kozlov (RKV) + * + */ +public class TestStudyDAO extends BaseTest { + + /** + * Logger for the class. + */ + private static final AppLogger LOG = AppLogger + .getLogger(TestStudyDAO.class); + + /** + * The tested StudyDAO. Later injected by Spring. + */ + @Autowired + @Qualifier("studyDAO") + private transient StudyDAO _studyDAO; + + /** + * The PublicationService. Later injected by Spring. + */ + @Autowired + @Qualifier("publicationService") + private transient PublicationService _publicationService; + + /** + * The StepService. Later injected by Spring. + */ + @Autowired + @Qualifier("stepService") + private transient StepService _stepService; + + /** + * The ProjectSettingsService. Later injected by Spring. + */ + @Autowired + @Qualifier("projectSettings") + private transient ProjectSettingsService _projectSettings; + + /** + * The DocumentTypeService. Later injected by Spring. + */ + @Autowired + @Qualifier("documentTypeService") + private transient DocumentTypeService _documentTypeService; + + /** + * Test creation of a study.
    + * Description :
    + * Create a study.
    + * Action :
    + * 1. call DAO's create method for a good transient study.
    + * 2. call DAO's create method for a study with non-zero id.
    + * Test data :
    + * no input parameters
    + * no input parameters
    + * + * Outcome results:
    + * + *
      + *
    • Object is created in the database successfully
      + *
    • + *
    • Another new object is created with new generated id
      + *
    • + *
    + *
    + * + * @throws InvalidPropertyException + * if an invalid property is used when creating objects + * @throws MultiplyDefinedException + * when trying to create an object with already existing id + * @throws MissedPropertyException + * if a mandatory property is not defined for an object to be created + * @throws SQLException + * @throws IOException + * + */ + @Test + public void testCreate() throws InvalidPropertyException, + MissedPropertyException, MultiplyDefinedException, IOException, + SQLException { + LOG.debug(">>>>> BEGIN testCreate()"); + startNestedTransaction(); + + Study aStudy = createStudy(); + // Call DAO's create method for a good transient study. + long id = _studyDAO.create(aStudy); + Assert.assertNotNull(id, + "Create method returns null instead of a new id."); + Assert.assertTrue(id > 0, "The new id is not a positive number."); + + LOG.debug("Created id: " + id); + + Study aStudyFound = getHibernateTemplate().get(Study.class, id); + _studyDAO.flush(); + + // Call DAO's create method for a study with non-zero id. + Study aBadStudy = new Study((new Study.Properties()).setTitle( + "2.Bad test study").setManager(aStudy.getAuthor()).setDate( + aStudy.getDate()).setReference("sid")); + aBadStudy.setIndex(aStudyFound.getIndex()); + + _studyDAO.flush(); + List res = _studyDAO.getAll(Order.asc("rid")); + + Assert.assertNotNull(res, + "Method getFilteredList must not return null."); + Assert.assertEquals(res.size(), 1, "Number of found objects is wrong."); + Assert.assertEquals(aBadStudy.getIndex(), id, "Id must be equal."); + + Assert.assertNotNull(aBadStudy.getIndex(), + "Create method returns null instead of a new id."); + Assert.assertTrue(aBadStudy.getIndex() > 0, + "The new id is not a positive number."); + + long id2 = _studyDAO.create(aBadStudy); + + _studyDAO.flush(); + + LOG.debug("Created second id: " + id2); + + res = _studyDAO.getAll(); + _studyDAO.flush(); + + Assert.assertNotNull(res, + "Method getFilteredList must not return null."); + Assert.assertNotNull(res.get(0), + "Method getFilteredList must not return null."); + Assert.assertNotNull(res.get(1), + "Method getFilteredList must not return null."); + Assert.assertEquals(res.size(), 2, "Number of found objects is wrong."); + Assert.assertEquals(res.get(0).getIndex(), id, + "Sorting is incorrect or id of the first element is wrong."); + Assert.assertEquals(res.get(1).getIndex(), id2, + "Sorting is incorrect or id of the second element is wrong."); + id = res.get(0).getIndex(); + id2 = res.get(1).getIndex(); + Assert.assertFalse(id == id2, "Ids of objects must not be duplicated."); + + rollbackNestedTransaction(); + LOG.debug(">>>>> END testCreate()"); + } + + /** + * Test of getting a study.
    + * Description :
    + * Create a study and try to get it from the database.
    + * Action :
    + * 1. call DAO's read method for an existing id.
    + * 2. call DAO's read method for a not existing id.
    + * Test data :
    + * no input parameters
    + * no input parameters
    + * + * Outcome results:
    + * + *
      + *
    • Object is found in the database successfully
      + *
    • + *
    • Result of search is null
      + *
    • + *
    + *
    + * + * @throws InvalidPropertyException + * if an invalid property is used when creating objects + * @throws MultiplyDefinedException + * when trying to create an object with already existing id + * @throws MissedPropertyException + * if a mandatory property is not defined for an object to be created + * @throws SQLException + * @throws IOException + * + */ + @Test + public void testGet() throws InvalidPropertyException, + MissedPropertyException, MultiplyDefinedException, IOException, + SQLException { + LOG.debug(">>>>> BEGIN testGet()"); + startNestedTransaction(); + + Study aStudy = createStudy(); + // Call DAO's create method for a good transient study. + Long id = aStudy.getIndex(); + Assert.assertNotNull(id, + "Create method returns null instead of a new id."); + Assert.assertTrue(id > 0, "The new id is not a positive number."); + + // Call DAO's get method for an existing id. + _studyDAO.flush(); + getHibernateTemplate().evict(aStudy); + getHibernateTemplate().clear(); + Study aStudyFound = _studyDAO.get(id); + + // Call DAO's get method for a not existing id. + aStudyFound = _studyDAO.get(-1L); + getHibernateTemplate().flush(); + Assert.assertNull(aStudyFound, + "A found object with not existing id must be null."); + + aStudyFound = _studyDAO.get(0L); + getHibernateTemplate().flush(); + Assert.assertNull(aStudyFound, + "A found object with not existing id must be null."); + + aStudyFound = _studyDAO.get(id + 1); + getHibernateTemplate().flush(); + Assert.assertNull(aStudyFound, + "A found object with not existing id must be null."); + + rollbackNestedTransaction(); + LOG.debug(">>>>> END testGet()"); + } + + /** + * Test of updating a study.
    + * Description :
    + * Create a study and try to update it with another data.
    + * Action :
    + * 1. call DAO's update method for an existing id.
    + * 2. call DAO's update method for a not existing id.
    + * 3. call DAO's update method for wrong data.
    + * Test data :
    + * no input parameters
    + * no input parameters
    + * no input parameters
    + * + * Outcome results:
    + * + *
      + *
    • Object is update in the database successfully
      + *
    • + *
    • Exception is thrown
      + *
    • + *
    • Exception is thrown
      + *
    • + *
    + *
    + * + * @throws InvalidPropertyException + * if an invalid property is used when creating objects + * @throws MultiplyDefinedException + * when trying to create an object with already existing id + * @throws MissedPropertyException + * if a mandatory property is not defined for an object to be created + * @throws SQLException + * @throws IOException + * + */ + @Test + public void testUpdate() throws InvalidPropertyException, + MissedPropertyException, MultiplyDefinedException, IOException, + SQLException { + LOG.debug(">>>>> BEGIN testUpdate()"); + startNestedTransaction(); + + Study aStudy = createStudy(); + // Call DAO's create method for a good transient study. + Long id = _studyDAO.create(aStudy); + Assert.assertNotNull(id, + "Create method returns null instead of a new id."); + Assert.assertTrue(id > 0, "The new id is not a positive number."); + + // Call DAO's update method for an existing id. + Assert.assertTrue(aStudy.getProgressState() != ProgressState.APPROVED, + "The initial state of the study should not be APPROVED."); + aStudy.setProgressState(ProgressState.APPROVED); + aStudy.setTitle(aStudy.getTitle() + " updated"); + _studyDAO.update(aStudy); + + // Check that the object has been updated. + Study aStudyFound = _studyDAO.get(id); + + // Call DAO's create method for a study with non-zero id. + Study aBadStudy = new Study((new Study.Properties()).setTitle( + "Test study").setManager(aStudy.getAuthor()).setDate( + aStudy.getDate())); + // aBadKelm.setIndex(aKelmFound.getIndex()); + try { + _studyDAO.update(aBadStudy); + getHibernateTemplate().flush(); + Assert.fail("Update with not existing id must be failed."); + } catch (Exception e) { + LOG.debug("Expected exception is thrown: " + + e.getClass().getSimpleName() + ": " + e.getMessage()); + } + // Call update with bad data (null title). + aBadStudy.setIndex(aStudyFound.getIndex()); + aBadStudy.setTitle(null); + try { + _studyDAO.update(aBadStudy); + getHibernateTemplate().flush(); + Assert.fail("Update with null title must be failed."); + } catch (Exception e) { + LOG.debug("Expected exception is thrown: " + + e.getClass().getSimpleName() + ": " + e.getMessage()); + } + // Call update with bad data (null state). + aBadStudy.setTitle(aStudyFound.getTitle()); + aBadStudy.setProgressState(null); + try { + _studyDAO.update(aBadStudy); + getHibernateTemplate().flush(); + Assert.fail("Update with null state must be failed."); + } catch (Exception e) { + LOG.debug("Expected exception is thrown: " + + e.getClass().getSimpleName() + ": " + e.getMessage()); + } + + rollbackNestedTransaction(); + LOG.debug(">>>>> END testUpdate()"); + } + + /** + * Test of deleting a study.
    + * Description :
    + * Create a study and try to delete it.
    + * Action :
    + * 1. call DAO's delete method for an existing id.
    + * 2. call DAO's delete method for a not existing id.
    + * Test data :
    + * no input parameters
    + * no input parameters
    + * + * Outcome results:
    + * + *
      + *
    • Object is found in the database successfully
      + *
    • + *
    • Exception is thrown
      + *
    • + *
    + *
    + * + * @throws InvalidPropertyException + * if an invalid property is used when creating objects + * @throws MultiplyDefinedException + * when trying to create an object with already existing id + * @throws MissedPropertyException + * if a mandatory property is not defined for an object to be created + * @throws SQLException + * @throws IOException + * + */ + @Test + public void testDelete() throws InvalidPropertyException, + MissedPropertyException, MultiplyDefinedException, IOException, + SQLException { + LOG.debug(">>>>> BEGIN testDelete()"); + startNestedTransaction(); + + Study aStudy = createStudy(); + // Call DAO's create method for a good transient study. + Long id = aStudy.getIndex();//_studyDAO.create(aStudy); + Assert.assertNotNull(id, + "Create method returns null instead of a new id."); + Assert.assertTrue(id > 0, "The new id is not a positive number."); + _studyDAO.flush(); + + // Call DAO's delete method for an existing id. + _studyDAO.merge(aStudy); + _studyDAO.delete(aStudy); + + // Check that the object has been deleted. + Study aStudyFound = _studyDAO.get(id); + + Assert.assertNull(aStudyFound, "Deleted object must not be found."); + // Call DAO's delete method for a not existing id. + getHibernateTemplate().flush(); + + rollbackNestedTransaction(); + LOG.debug(">>>>> END testDelete()"); + } + + /** + * Test of getting a filtered list of studys.
    + * Description :
    + * Create two studys and try to get them from the database.
    + * Action :
    + * 1. Find objects with the given value of the property of a child object.
    + * 2. Get ordered list of objects with the given value of the property of a child object.
    + * Test data :
    + * no input parameters
    + * no input parameters
    + * + * Outcome results:
    + * + *
      + *
    • Objects are found in the database successfully
      + *
    • + *
    • Objects are found in the database and sorted in the right order
      + *
    • + *
    + *
    + * + * @throws InvalidPropertyException + * if an invalid property is used when creating objects + * @throws MultiplyDefinedException + * when trying to create an object with already existing id + * @throws MissedPropertyException + * if a mandatory property is not defined for an object to be created + * @throws SQLException + * @throws IOException + * + */ + @Test + public void testGetFilteredList() throws InvalidPropertyException, + MissedPropertyException, MultiplyDefinedException, IOException, + SQLException { + LOG.debug(">>>>> BEGIN testGetFilteredList()"); + startNestedTransaction(); + + // Create several objects in the database + Study aStudy = createStudy(); + long id = _studyDAO.create(aStudy); + Study aNewStudy = new Study((new Study.Properties()).setTitle( + "TST_Study N2").setManager(aStudy.getAuthor()).setReference( + "sid").setDate(aStudy.getDate())); + long id2 = _studyDAO.create(aNewStudy); + + _studyDAO.flush(); + + // //////////////////////////////////////////////////// + // Call DAO's getFilteredList method without order parameter. + List res = _studyDAO.getFilteredList("manager", Restrictions.eq( + "username", aStudy.getAuthor().getUsername())); + + Assert.assertNotNull(res, + "Method getFilteredList must not return null."); + Assert.assertEquals(res.size(), 2, "Number of found objects is wrong."); + + // //////////////////////////////////////////////////// + // Call DAO's getFilteredList method with defined order parameter. + res = _studyDAO.getFilteredList("manager", Restrictions.eq("username", + aStudy.getAuthor().getUsername()), Order.asc("title")); + + Assert.assertNotNull(res, + "Method getFilteredList must not return null."); + Assert.assertEquals(res.size(), 2, "Number of found objects is wrong."); + Assert.assertEquals(res.get(0).getIndex(), id, + "Sorting of results is not correct."); + Assert.assertEquals(res.get(1).getIndex(), id2, + "Sorting of results is not correct."); + + rollbackNestedTransaction(); + LOG.debug(">>>>> END testGetFilteredList()"); + } + + /** + * Create a persistent scenario for tests. + * + * @return a persistent scenario + * @throws InvalidPropertyException + * if an invalid property is used when creating objects + * @throws MultiplyDefinedException + * when trying to create an object with already existing id + * @throws MissedPropertyException + * if a mandatory property is not defined for an object to be created + * @throws IOException + * if document creation is failed + * @throws SQLException + * if project settings loading is failed + */ + private Study createStudy() throws InvalidPropertyException, + MissedPropertyException, MultiplyDefinedException, IOException, + SQLException { + // Create a scenario for tests + HibernateTemplate ht = getHibernateTemplate(); + + Database.getInstance().reset(); + _projectSettings.getAllSteps().clear(); // Clear config to be able to load it again + // Load workflow customization + try { + _projectSettings.configure(ClassLoader.getSystemResource( + "test/som.xml").getPath()); + } catch (FileNotFoundException e) { + Assert.fail("Can't find som.xml: ", e); + } + List steps = _projectSettings.getAllSteps(); + Assert.assertTrue(steps.size() > 0, "No steps are created."); + + // Create a test user + User.Properties uprop = new User.Properties(); + uprop.setUsername("TST_Username").setName("TST_SimanUnitTestsUser") + .setFirstName("TST_FirstName").setDisplayName("TST_test.user") + .addRole("TST_user").setMailAddress( + "noreply@salome-platform.org"); + uprop.disableCheck(); + User anAuthor = new User(uprop); + ht.saveOrUpdate(anAuthor); + + // Create a test study + Study.Properties stprops = new Study.Properties().setReference( + "TST_SID_01").setTitle("TST_Study").setManager(anAuthor); + Study aStudy = new Study(stprops); + ht.saveOrUpdate(aStudy); + + // Create a test scenario + Scenario.Properties sprops = new Scenario.Properties().setTitle( + "TST_Scenario").setManager(anAuthor).setOwnerStudy(aStudy); + Scenario aScenario = new Scenario(sprops); + aStudy.getScenariiList().add(aScenario); + ht.saveOrUpdate(anAuthor); + ht.saveOrUpdate(aStudy); + ht.saveOrUpdate(aScenario); + + // Create documents for each scenario step + Document.Properties dprop = new Document.Properties().setAuthor( + anAuthor).setDate(new Date()); + int i = 0; + Publication usedPub = null; + Map usedMap = new HashMap(); + for (int stepNum = 1; stepNum <= steps.size(); stepNum++) { + Step step = _projectSettings.getStep(stepNum); + LOG.debug("Create scenario step: " + stepNum); + ProjectElement projElem; + + if (step.appliesTo(Study.class)) { + projElem = aStudy; + } else { + projElem = aScenario; + } + org.splat.som.Step aScStep = new org.splat.som.Step(step, projElem); + List dtypes = _documentTypeService + .selectTypesOf(step); + if (dtypes.size() > 0) { + DocumentType dtype = dtypes.get(0); + // Create a document published in the scenario + // document: document type[0] - first type used on the step + // .brep + // .med + i++; + dprop.setName("document" + stepNum).setType(dtype); + if (step.getNumber() > 3) { + dprop.setFormat("med"); + } else { + dprop.setFormat("py"); + } + Publication pub = createDoc(projElem, aScStep, dprop, "med", + false); + if (usedPub != null) { + pub.addDependency(usedPub); + LOG.debug("Add dependency: " + pub.value().getTitle() + + " from " + usedPub.value().getTitle()); + ht.saveOrUpdate(pub.value()); + ht.flush(); + + usedMap.put(pub.getIndex(), usedPub.getIndex()); + } + usedPub = pub; + } + if (dtypes.size() <= 0) { + LOG.debug("No document types are found for scenario step " + i); + } + } + + // Check that the scenario and its documents have been created correctly. + + Assert.assertNotNull(ht.find("from Document"), + "No documents in the database."); + Assert.assertTrue(ht.find("from Document").size() > 0, + "No documents in the database."); + + Assert.assertNotNull(ht.find("from Publication where owner=" + + aScenario.getIndex()), "No publications in the database."); + Assert.assertTrue( + ht.find("from Publication where owner=" + aScenario.getIndex()) + .size() > 0, "No publications in the database."); + + for (Publication p : (List) ht + .find("from Publication where owner=" + aScenario.getIndex())) { + LOG.debug("Publication found: [id=" + p.getIndex() + ", owner=" + + p.getOwner().getIndex() + ", doc=" + p.value().getIndex() + + "]"); + Assert.assertEquals(p.getOwner().getIndex(), aScenario.getIndex(), + "The publication was not attached to the scenario."); + } + + // Remove the scenario from the current hibernate session. + ht.evict(aScenario); + // Check that the scenario is created in the database. + Scenario aScen = ht.load(Scenario.class, aScenario.getIndex()); + Assert.assertNotNull(aScen, "Scenario was not saved in the database."); + Assert.assertTrue(aScen.getDocums().size() > 0, + "No publications in the scenario."); + + Assert.assertTrue(i > 0, + "More then one document must be in the database"); + + // Check created uses relations + Assert + .assertTrue(usedMap.size() > 0, + "Uses relations must be created."); + boolean foundAny = false; + for (Long usingId : usedMap.keySet()) { + for (Publication pub : aScen.getDocums()) { + if (pub.getIndex() == usingId) { + boolean found = false; + for (Publication used : aScen.getDocums()) { + found = (used.getIndex() == usedMap.get(usingId)); + if (found) { + break; + } + } + if (!found) { + for (Publication used : aStudy.getDocums()) { + found = (used.getIndex() == usedMap.get(usingId)); + if (found) { + break; + } + } + } + Assert.assertTrue(found, + "Uses relation was not created in the database."); + foundAny = foundAny || found; + } + } + } + Assert.assertTrue(foundAny, + "No Uses relation was created in the database."); + + return aScenario.getOwnerStudy(); + } + + /** + * Create a document published in the scenario.
    + * document:
    + * document type - type used on the step
    + * <source-file>.brep
    + * <attached-file>.med + * + * @param aScenario + * the scenario to add the document to + * @param aScStep + * scenario step where the document to be published + * @param dprop + * document properties + * @param attachedFileExt + * extension of the secon attached (exported) file + * @param isOutdated + * outdated document flag + * @return the publication of the created document + * @throws IOException + * @throws MultiplyDefinedException + * @throws InvalidPropertyException + * @throws MissedPropertyException + */ + private Publication createDoc(final ProjectElement aScenario, + final org.splat.som.Step aScStep, final Properties dprop, + final String attachedFileExt, final boolean isOutdated) + throws MissedPropertyException, InvalidPropertyException, + MultiplyDefinedException, IOException { + // Create a document published in the scenario + // document: document type - type used on the step + // .brep + // .med + Publication pub = _stepService.createDocument(aScStep, dprop); + Assert.assertNotNull(pub.getOwner(), + "The publication must be attached to the scenario."); + Assert.assertEquals(pub.getOwner().getIndex(), aScenario.getIndex(), + "The publication was not attached to the scenario."); + + if (isOutdated) { + pub.setIsnew('O'); + } + aScenario.add(pub); + HibernateTemplate ht = getHibernateTemplate(); + ht.saveOrUpdate(pub); + + // Attach a file + ht.save(pub.value()); + ht.flush(); + ht.saveOrUpdate(_publicationService.attach(pub, attachedFileExt)); + + return pub; + } +} diff --git a/Workspace/Siman-Common/src/test/splat/service/TestStepService.java b/Workspace/Siman-Common/src/test/splat/service/TestStepService.java new file mode 100644 index 0000000..dd9b728 --- /dev/null +++ b/Workspace/Siman-Common/src/test/splat/service/TestStepService.java @@ -0,0 +1,548 @@ +/***************************************************************************** + * Company OPEN CASCADE + * Application SIMAN + * File $Id$ + * Creation date 12 Oct 2012 + * @author $Author$ + * @version $Revision$ + *****************************************************************************/ +package test.splat.service; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.sql.SQLException; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.splat.dal.bo.kernel.User; +import org.splat.dal.bo.som.Document; +import org.splat.dal.bo.som.DocumentType; +import org.splat.dal.bo.som.ProjectElement; +import org.splat.dal.bo.som.Publication; +import org.splat.dal.bo.som.Scenario; +import org.splat.dal.bo.som.Study; +import org.splat.dal.bo.som.UsedByRelation; +import org.splat.dal.bo.som.UsesRelation; +import org.splat.dal.bo.som.Document.Properties; +import org.splat.dal.dao.som.Database; +import org.splat.dal.dao.som.ScenarioDAO; +import org.splat.kernel.InvalidPropertyException; +import org.splat.kernel.MismatchException; +import org.splat.kernel.MissedPropertyException; +import org.splat.kernel.MultiplyDefinedException; +import org.splat.kernel.NotApplicableException; +import org.splat.log.AppLogger; +import org.splat.service.DocumentTypeService; +import org.splat.service.KnowledgeElementTypeService; +import org.splat.service.ProjectElementService; +import org.splat.service.PublicationService; +import org.splat.service.ScenarioService; +import org.splat.service.SimulationContextService; +import org.splat.service.StepService; +import org.splat.service.StudyService; +import org.splat.service.technical.ProjectSettingsService; +import org.splat.service.technical.RepositoryService; +import org.splat.service.technical.ProjectSettingsService.Step; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.orm.hibernate3.HibernateTemplate; +import org.testng.Assert; +import org.testng.annotations.Test; + +import test.splat.common.BaseTest; + +/** + * Test class for StepService. + * + * @author Roman Kozlov (RKV) + * + */ +public class TestStepService extends BaseTest { + + /** + * Logger for the class. + */ + private static final AppLogger LOG = AppLogger + .getLogger(TestStepService.class); + + /** + * The StudyService. Later injected by Spring. + */ + @Autowired + @Qualifier("studyService") + private transient StudyService _studyService; + + /** + * The ScenarioService. Later injected by Spring. + */ + @Autowired + @Qualifier("scenarioService") + private transient ScenarioService _scenarioService; + + /** + * The ProjectElementService. Later injected by Spring. + */ + @Autowired + @Qualifier("projectElementService") + private transient ProjectElementService _projectElementService; + + /** + * The RepositoryService. Later injected by Spring. + */ + @Autowired + @Qualifier("repositoryService") + private transient RepositoryService _repositoryService; + + /** + * The Scenario DAO. Later injected by Spring. + */ + @Autowired + @Qualifier("scenarioDAO") + private transient ScenarioDAO _scenarioDAO; + + /** + * The PublicationService. Later injected by Spring. + */ + @Autowired + @Qualifier("publicationService") + private transient PublicationService _publicationService; + + /** + * The StepService. Later injected by Spring. + */ + @Autowired + @Qualifier("stepService") + private transient StepService _stepService; + + /** + * The SimulationContextService. Later injected by Spring. + */ + @Autowired + @Qualifier("simulationContextService") + private transient SimulationContextService _simulationContextService; + + /** + * The ProjectSettingsService. Later injected by Spring. + */ + @Autowired + @Qualifier("projectSettings") + private transient ProjectSettingsService _projectSettings; + + /** + * The DocumentTypeService. Later injected by Spring. + */ + @Autowired + @Qualifier("documentTypeService") + private transient DocumentTypeService _documentTypeService; + + /** + * The KnowledgeElementTypeService. Later injected by Spring. + */ + @Autowired + @Qualifier("knowledgeElementTypeService") + private transient KnowledgeElementTypeService _knowledgeElementTypeService; + + /** + * Test check-in scenario operation to be performed after SALOME session.
    + * Description :
    + * Create a scenario and try to check-in it with some simulated SALOME results data.
    + * After check-in verify following points: + *
      + *
    • scenario is no more marked as checked out
    • + *
    • new document versions are created for checked in documents
    • + *
    • presentation of the previous version is removed
    • + *
    • uses relations are copied correctly
    • + *
    • files are moved correctly
    • + *
    • formats of files are new if they are according to the document's type on the study step
    • + *
    • new documents are created for new data
    • + *
    • new documents have correctly generated names
    • + *
    • uses relations are created correctly
    • + *
    • files are moved correctly
    • + *
    + *

    + * Action :
    + * 1. call the method for an existing scenario id.
    + * 2. call the method for a not existing scenario id.
    + * Test data :
    + * no input parameters
    + * no input parameters
    + * + * Outcome results:
    + * + *
      + *
    • New version of existing documents must be created and new documents must be imported for documents with zero id. Correct + * relations must be created.
      + *
    • + *
    • Exception is thrown
      + *
    • + *
    + *
    + * + * @throws InvalidPropertyException + * if an invalid property is used when creating objects + * @throws MultiplyDefinedException + * when trying to create an object with already existing id + * @throws MissedPropertyException + * if a mandatory property is not defined for an object to be created + * @throws IOException + * if scenario creation is failed + * @throws SQLException + * if scenario creation is failed + * @throws NotApplicableException + * if checkin failed + * @throws MismatchException + * if checkin failed + */ + @Test + public void testRemoveDocument() throws InvalidPropertyException, + MissedPropertyException, MultiplyDefinedException, IOException, + SQLException, MismatchException, NotApplicableException { + LOG.debug(">>>>> BEGIN testRemoveDocument()"); + startNestedTransaction(); + + _projectSettings.getAllSteps().clear(); // Clear config to be able to load it again + _projectSettings.configure(ClassLoader + .getSystemResource("test/som.xml").getPath()); + HibernateTemplate ht = getHibernateTemplate(); + ht.flush(); + long scenarioId = createScenario(); + ht.flush(); + _scenarioDAO.flush(); + Scenario aScen = _scenarioDAO.get(scenarioId); + + Assert.assertTrue(ht.find("from UsesRelation").size() > 0, + "Uses relations were not created in the database."); + + Assert.assertTrue(ht.find("from UsedByRelation").size() > 0, + "UsedBy relations were not created in the database."); + + // //////////////////////////////////////////////////////// + // Call removeDocument method for each document of the + // study/scenario starting from the last document. + + ProjectElement projElem; + boolean ok; + long docId; + Step step; + for (int i = _projectSettings.getAllSteps().size(); i > 0; i--) { + LOG.debug("Remove documents from the step " + i); + step = _projectSettings.getStep(i); + if (step.appliesTo(Study.class)) { + projElem = _studyService.selectStudy(aScen.getOwnerStudy() + .getIndex()); + } else { + projElem = aScen; + } + + org.splat.som.Step aScStep = new org.splat.som.Step(step, projElem); + + if (aScStep.getDocuments().size() > 0) { + docId = aScStep.getDocuments().get(0).value().getIndex(); + LOG.debug("Remove document " + + aScStep.getDocument(docId).value().getTitle()); + ht.flush(); + List uses = ht + .find("from UsesRelation where owner=" + docId); + List usedBy = ht + .find("from UsedByRelation where owner=" + docId); + LOG.debug("From db: It uses following " + uses.size() + + " documents: "); + for (UsesRelation rel : uses) { + LOG.debug(rel.getTo().getTitle()); + } + LOG.debug("From step: It uses following " + + aScStep.getDocument(docId).getRelations( + UsesRelation.class).size() + " documents: "); + for (Publication rel : aScStep.getDocument(docId).getRelations( + UsesRelation.class)) { + LOG.debug(rel.value().getTitle()); + } + LOG.debug("From db: It is used by following " + usedBy.size() + + " documents: "); + for (UsedByRelation rel : usedBy) { + LOG.debug(rel.getTo().getTitle()); + } + LOG.debug("From step: It is used by following " + + aScStep.getDocument(docId).getRelations( + UsedByRelation.class).size() + " documents: "); + for (Publication rel : aScStep.getDocument(docId).getRelations( + UsedByRelation.class)) { + LOG.debug(rel.value().getTitle()); + } + ok = _stepService.removeDocument(aScStep, docId); + + Assert.assertTrue(ok, "Removing was failed."); + _scenarioDAO.flush(); + ht.flush(); + Assert.assertEquals(ht.find( + "from UsesRelation where owner=" + docId).size(), 0, + "UsesRelation(s) were not removed from the database."); + Assert + .assertEquals(ht.find( + "from UsedByRelation where owner=" + docId) + .size(), 0, + "UsedByRelation(s) were not removed from the database."); + Assert.assertEquals(ht.find( + "from UsesRelation where refer=" + docId).size(), 0, + "Referencing UsesRelation(s) were not removed from the database."); + Assert + .assertEquals(ht.find( + "from UsedByRelation where refer=" + docId) + .size(), 0, + "Referencing UsedByRelation(s) were not removed from the database."); + Assert + .assertEquals(ht.find( + "from ConvertsRelation where owner=" + docId) + .size(), 0, + "ConvertsRelation(s) were not removed from the database."); + } + } + + Assert.assertEquals(ht.find("from Document").size(), 0, + "Documents were not removed from the database."); + + Assert.assertEquals(ht.find( + "from Publication where owner=" + aScen.getIndex()).size(), 0, + "Publications were not removed from the database."); + + Assert.assertEquals(ht.find("from UsesRelation").size(), 0, + "Uses relations were not removed from the database."); + + Assert.assertEquals(ht.find("from UsedByRelation").size(), 0, + "UsedBy relations were not removed from the database."); + + rollbackNestedTransaction(); + LOG.debug(">>>>> END testRemoveDocument()"); + } + + /** + * Create a persistent scenario for tests. + * + * @return a persistent scenario + * @throws InvalidPropertyException + * if an invalid property is used when creating objects + * @throws MultiplyDefinedException + * when trying to create an object with already existing id + * @throws MissedPropertyException + * if a mandatory property is not defined for an object to be created + * @throws IOException + * if document creation is failed + * @throws SQLException + * if project settings loading is failed + */ + private long createScenario() throws InvalidPropertyException, + MissedPropertyException, MultiplyDefinedException, IOException, + SQLException { + // Create a scenario for tests + HibernateTemplate ht = getHibernateTemplate(); + + Database.getInstance().reset(); + _projectSettings.getAllSteps().clear(); // Clear config to be able to load it again + // Load workflow customization + try { + _projectSettings.configure(ClassLoader.getSystemResource( + "test/som.xml").getPath()); + } catch (FileNotFoundException e) { + Assert.fail("Can't find som.xml: ", e); + } + List steps = _projectSettings.getAllSteps(); + Assert.assertTrue(steps.size() > 0, "No steps are created."); + + // Create a test user + User.Properties uprop = new User.Properties(); + uprop.setUsername("TST_Username").setName("TST_SimanUnitTestsUser") + .setFirstName("TST_FirstName").setDisplayName("TST_test.user") + .addRole("TST_user").setMailAddress( + "noreply@salome-platform.org"); + uprop.disableCheck(); + User anAuthor = new User(uprop); + ht.saveOrUpdate(anAuthor); + + // Create a test study + Study.Properties stprops = new Study.Properties().setReference( + "TST_SID_01").setTitle("TST_Study").setManager(anAuthor); + Study aStudy = new Study(stprops); + ht.saveOrUpdate(aStudy); + + // Create a test scenario + Scenario.Properties sprops = new Scenario.Properties().setTitle( + "TST_Scenario").setManager(anAuthor).setOwnerStudy(aStudy); + Scenario aScenario = new Scenario(sprops); + aStudy.getScenariiList().add(aScenario); + ht.saveOrUpdate(anAuthor); + ht.saveOrUpdate(aStudy); + ht.saveOrUpdate(aScenario); + + // Create documents for each scenario step + Document.Properties dprop = new Document.Properties().setAuthor( + anAuthor).setDate(new Date()); + int i = 0; + Publication usedPub = null; + Map usedMap = new HashMap(); + for (int stepNum = 1; stepNum <= steps.size(); stepNum++) { + Step step = _projectSettings.getStep(stepNum); + LOG.debug("Create scenario step: " + stepNum); + ProjectElement projElem; + + if (step.appliesTo(Study.class)) { + projElem = aStudy; + } else { + projElem = aScenario; + } + org.splat.som.Step aScStep = new org.splat.som.Step(step, projElem); + List dtypes = _documentTypeService + .selectTypesOf(step); + if (dtypes.size() > 0) { + DocumentType dtype = dtypes.get(0); + // Create a document published in the scenario + // document: document type[0] - first type used on the step + // .brep + // .med + i++; + dprop.setName("document" + stepNum).setType(dtype); + if (step.getNumber() > 3) { + dprop.setFormat("med"); + } else { + dprop.setFormat("py"); + } + Publication pub = createDoc(projElem, aScStep, dprop, "med", + false); + if (usedPub != null) { + pub.addDependency(usedPub); + LOG.debug("Add dependency: " + pub.value().getTitle() + + " from " + usedPub.value().getTitle()); + ht.saveOrUpdate(pub.value()); + ht.flush(); + + usedMap.put(pub.getIndex(), usedPub.getIndex()); + } + usedPub = pub; + } + if (dtypes.size() <= 0) { + LOG.debug("No document types are found for scenario step " + i); + } + } + + // Check that the scenario and its documents have been created correctly. + + Assert.assertNotNull(ht.find("from Document"), + "No documents in the database."); + Assert.assertTrue(ht.find("from Document").size() > 0, + "No documents in the database."); + + Assert.assertNotNull(ht.find("from Publication where owner=" + + aScenario.getIndex()), "No publications in the database."); + Assert.assertTrue( + ht.find("from Publication where owner=" + aScenario.getIndex()) + .size() > 0, "No publications in the database."); + + for (Publication p : (List) ht + .find("from Publication where owner=" + aScenario.getIndex())) { + LOG.debug("Publication found: [id=" + p.getIndex() + ", owner=" + + p.getOwner().getIndex() + ", doc=" + p.value().getIndex() + + "]"); + Assert.assertEquals(p.getOwner().getIndex(), aScenario.getIndex(), + "The publication was not attached to the scenario."); + } + + // Remove the scenario from the current hibernate session. + ht.evict(aScenario); + // Check that the scenario is created in the database. + Scenario aScen = ht.load(Scenario.class, aScenario.getIndex()); + Assert.assertNotNull(aScen, "Scenario was not saved in the database."); + Assert.assertTrue(aScen.getDocums().size() > 0, + "No publications in the scenario."); + + Assert.assertTrue(i > 0, + "More then one document must be in the database"); + + // Check created uses relations + Assert + .assertTrue(usedMap.size() > 0, + "Uses relations must be created."); + boolean foundAny = false; + for (Long usingId : usedMap.keySet()) { + for (Publication pub : aScen.getDocums()) { + if (pub.getIndex() == usingId) { + boolean found = false; + for (Publication used : aScen.getDocums()) { + found = (used.getIndex() == usedMap.get(usingId)); + if (found) { + break; + } + } + if (!found) { + for (Publication used : aStudy.getDocums()) { + found = (used.getIndex() == usedMap.get(usingId)); + if (found) { + break; + } + } + } + Assert.assertTrue(found, + "Uses relation was not created in the database."); + foundAny = foundAny || found; + } + } + } + Assert.assertTrue(foundAny, + "No Uses relation was created in the database."); + + return aScenario.getIndex(); + } + + /** + * Create a document published in the scenario.
    + * document:
    + * document type - type used on the step
    + * <source-file>.brep
    + * <attached-file>.med + * + * @param aScenario + * the scenario to add the document to + * @param aScStep + * scenario step where the document to be published + * @param dprop + * document properties + * @param attachedFileExt + * extension of the secon attached (exported) file + * @param isOutdated + * outdated document flag + * @return the publication of the created document + * @throws IOException + * @throws MultiplyDefinedException + * @throws InvalidPropertyException + * @throws MissedPropertyException + */ + private Publication createDoc(final ProjectElement aScenario, + final org.splat.som.Step aScStep, final Properties dprop, + final String attachedFileExt, final boolean isOutdated) + throws MissedPropertyException, InvalidPropertyException, + MultiplyDefinedException, IOException { + // Create a document published in the scenario + // document: document type - type used on the step + // .brep + // .med + Publication pub = _stepService.createDocument(aScStep, dprop); + Assert.assertNotNull(pub.getOwner(), + "The publication must be attached to the scenario."); + Assert.assertEquals(pub.getOwner().getIndex(), aScenario.getIndex(), + "The publication was not attached to the scenario."); + + if (isOutdated) { + pub.setIsnew('O'); + } + aScenario.add(pub); + HibernateTemplate ht = getHibernateTemplate(); + ht.saveOrUpdate(pub); + + // Attach a file + ht.save(pub.value()); + ht.flush(); + ht.saveOrUpdate(_publicationService.attach(pub, attachedFileExt)); + + return pub; + } +} diff --git a/Workspace/Siman-Common/testng-stepService.xml b/Workspace/Siman-Common/testng-stepService.xml new file mode 100644 index 0000000..bafa7e3 --- /dev/null +++ b/Workspace/Siman-Common/testng-stepService.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Workspace/Siman/WebContent/WEB-INF/lib/jsonplugin-0.34.jar b/Workspace/Siman/WebContent/WEB-INF/lib/jsonplugin-0.34.jar new file mode 100644 index 0000000000000000000000000000000000000000..7edffdabe3ac148f20e4946621c9ee4a4ddbc8a0 GIT binary patch literal 47456 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 PFfecfp&8J|KCn>$d(&(N delta 519 zcmcZ=d^4Cgz?+$civa|_)^$zf)!_V6*X79H*7Ce^qFsl*Lo?48-rg_0JUqP>J1cfF zAXI+_Dg$eS5-dPEzbIWFMa5(`Mpb5@fs^$Z^+5FIc*ZhDFyjW(eikre7W-LdW}so4 zv$)R+gBe!x*O!>jKp}LqhLOllqNPTgM!809328J_0tPfYu zz_6q-5UAcx0i?z*DlNVq7!aNu3=FC$YECgtUZ^fQ`45v2hQSg$OKba?85pYA7#NgM zH1$lLs9~f43g`pzN0@tnu9Q(>U{FI*6T=NQxQ|(V~s*ct&wuZ|Z?7ZO#K^#Kk%@r; zJ&e@r&rX`?fbjCXZ82f=XJUkqr)-D=Z8gN+xG5Ja}!{?3eRqWS@iX#+RL(Q3mT_6McO{lWlL&k zR5{AH>7en_3jP^)w@Gx>H8MW`Z>e*1Ka;Y)+3~gAaa}qh&rDxG-B8(e{9bZNpND6B zB~SJd*4;IpM{=*L)weBZd9zNl?O}=fVI8}*FZSK|cH~CC&0g7u3bi?(1l!K@2*mAm zj$_;X^4}I8=2yJxr4g5lgWa=VNFIA+Sn1~3nsu^a$JKXpxo_-i-OU%?_OHftm4EKk zNvRWkw@?0f{{GK3rQQA71xp0p7X&ZxU3c%?J5y<%Jo~5TlrIwm@oVC>19XkR%S7<<3bV{h|nW^1PUL*YB#uZ@4b zI9+0o)5`DVXN`70tJe-~3fnXB)t!Rx*RI?OUd^(5+Q}E^ChwD!RbrC-XyR z8>PQlQMm5b*3EleE}h*sQMGDw`uwlUJ1j**OJ(x;b=G~}&MP3E{weNaTzBu&&L$70Z?dT}gTxux^+5DyBlb2%Fk?5zc@{9Em-j9+n2{@ZR~XFj zRei<;W|(PAXW;~z<_J#NldtJmDu8&%AteH~8AcpooLs0Yq8scM;LXS+!T>KM9FZL? z3R4ZH7fvqFRjCI$%@J9clYs?^+sMYi;OiRVsO#zHrvL90$j^*STnrFjgIt897DXci z!y2I0{GxPyd^)%oc##b_H(5bX66F8MHhR)b-`FNQvWrg6)8hdP)dGdo*?~falTSm1 zZUKchaDY|a=Mb7Krw>u7t1r#e#0wVQpwBWnSziKdKpjvxNf0V5uK@D6vR-0X8!$Hg zfPOYbb_s)%Dp+BZ2LI#+Rbg%9Pz7n^51zfq38-l=I|G9Tibn0pfdb@gX>hVfV zBc`RE34T|_?#wZC3!BiaqwEo;%+z{e>q~|kQ}z5pCvPjf&+%-XwWMiV&zfwrm>l-P zdA#ha5AEIE!DrYTsmL@XNwKohbn2?-W)22!e{wFJnYGh3`L1^N_D2>>yW_UloGUET zkvs5oiJe-;v3f<(llmzSJe1sD+HHyB-r%|7y43Qrzm~IPj_kLZ(>ncZ!pz-jxAlcy zE$3z_Pu#Rm|8?3I_CF=n`qL*}+HMAGOH=-ay*Ao@IHT$E z9OspGZtn#azWHJ?JJ4da=N$HbOUibCmNva9zvl->!TsAc@BZ-xcuTW$oM6qr+Q`Vj zaFL0D0X>-1>rdxBau9I+?(wHteDe%JBMAY0@68{AS(1HtZZw`rzMP(rtbI`u_Eib4cnG_=ECbD&IY%pI10%-?K}FqP3p(Q>zY+1o2qg?akegX z@Ofvi=&wHe;x~g>-yPTG(j1TJSBjlI@Vaci++^8v-&C|&gF@flV|D&0_|EE%(TC%S z`qm=9MQ1-s*&@dm=eII=$B)-sZ-Q3(@6b-+$-j3j>q&9fs`*971MhFXA2B`p{x0+X ze;1lRwOAIYFMHEEwd99L&byuKp0oVX|05gV&CYR3^!~n?z_|O39(SA9FvLE^REewBxx-re+U*3SEae{Y=n z`HWvoFM4(1?6)r`$;!`;JNNulXlzWO-rrrtH+lZ=SR?lPQ2R4}{~eMWU+9I*+!n5x zcwYorM#q%MqNgC*RPqln3#UgGvN!GmJPq*+5rW0i*`mg`zNN zFg=fXvZ1cXPpwwvoSFEx`sIFdiuHP|2qZp6C)EB1H@NAr#T`Uz=F(S zSOwIYUzDzoPY1*tn6@)e9mNGXi6vlt+%T(v4xenNC(ZPQZSqHb(a8mRJm9da2MVdO zgM}j4WhS463f}<=uj2p<^XbchRqE?YGd1vng%b5;z!A^@6iN^T3r!Xj)&{v#gaJ~1 zf_$c|ml)Otj7dMBw@hJ91k( + +<% for (Iterator listem = menu.asList().iterator(); listem.hasNext(); ) { MenuItem item = listem.next(); String arrow = "image.hold.gif"; @@ -89,4 +92,5 @@ <% } %> + \ No newline at end of file diff --git a/Workspace/Siman/WebContent/layout/homeLayout.jsp b/Workspace/Siman/WebContent/layout/homeLayout.jsp index b35caee..3de62f2 100644 --- a/Workspace/Siman/WebContent/layout/homeLayout.jsp +++ b/Workspace/Siman/WebContent/layout/homeLayout.jsp @@ -32,6 +32,24 @@ - + +

    + +
    \ No newline at end of file diff --git a/Workspace/Siman/WebContent/study/editScenarioProperties.jsp b/Workspace/Siman/WebContent/study/editScenarioProperties.jsp index ff01292..35a2a63 100644 --- a/Workspace/Siman/WebContent/study/editScenarioProperties.jsp +++ b/Workspace/Siman/WebContent/study/editScenarioProperties.jsp @@ -1,11 +1,5 @@ <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <%@ taglib prefix="s" uri="/struts-tags"%> - - - - - rel="stylesheet" type="text/css"> - - - - - @@ -77,6 +67,3 @@ - - - \ No newline at end of file diff --git a/Workspace/Siman/src/hibernate.properties b/Workspace/Siman/src/hibernate.properties index 88cd755..1d1587d 100644 --- a/Workspace/Siman/src/hibernate.properties +++ b/Workspace/Siman/src/hibernate.properties @@ -1,4 +1,4 @@ -# Generated at 04/12/2012 01:42:16 +# Generated at 20/12/2012 09:31:13 # Don't edit manually. See the source in D:\users\rkv\SIMAN\SIMAN_SRC\Workspace\Siman\conf\templates. # Connection properties connection.driver_class=com.mysql.jdbc.Driver diff --git a/Workspace/Siman/src/jdbc.properties b/Workspace/Siman/src/jdbc.properties index ecfd593..cb7201f 100644 --- a/Workspace/Siman/src/jdbc.properties +++ b/Workspace/Siman/src/jdbc.properties @@ -1,4 +1,4 @@ -# Generated at 04/12/2012 01:42:16 +# Generated at 20/12/2012 09:31:13 # Don't edit manually. See the source in D:\users\rkv\SIMAN\SIMAN_SRC\Workspace\Siman\conf\templates. # Connection properties connection.url=jdbc:mysql://localhost/simer diff --git a/Workspace/Siman/src/org/splat/simer/Action.java b/Workspace/Siman/src/org/splat/simer/Action.java index ff32355..16e0b27 100644 --- a/Workspace/Siman/src/org/splat/simer/Action.java +++ b/Workspace/Siman/src/org/splat/simer/Action.java @@ -178,7 +178,8 @@ public class Action extends ActionSupport implements ServletRequestAware, * Remove the currently open knowledge from the session. */ protected void closeKnowledge() { - AbstractOpenObject open = (AbstractOpenObject) _session.remove(KNOWLEDGE_OPEN); + AbstractOpenObject open = (AbstractOpenObject) _session + .remove(KNOWLEDGE_OPEN); if ((open != null) && (_session.get(STUDY_OPEN) == null)) { open.clearFacades(); // For eventually reopening the knowledge from a fresh context } @@ -188,7 +189,8 @@ public class Action extends ActionSupport implements ServletRequestAware, * Remove the currently open study from the session. */ protected void closeStudy() { - AbstractOpenObject open = (AbstractOpenObject) _session.remove(STUDY_OPEN); + AbstractOpenObject open = (AbstractOpenObject) _session + .remove(STUDY_OPEN); if ((open != null) && (_session.get(KNOWLEDGE_OPEN) == null)) { open.clearFacades(); // For eventually reopening the study from a fresh context } @@ -386,8 +388,10 @@ public class Action extends ActionSupport implements ServletRequestAware, Menu menu = (Menu) _session.get("menu." + leftMenuProperty); getLeftMenuSettings().setMenu(menu); - getLeftMenuSettings().setMenuName(menu.getName()); - getLeftMenuSettings().setMenuNamespace(menu.getNamespace()); + if (menu != null) { + getLeftMenuSettings().setMenuName(menu.getName()); + getLeftMenuSettings().setMenuNamespace(menu.getNamespace()); + } } /** @@ -403,7 +407,8 @@ public class Action extends ActionSupport implements ServletRequestAware, initializationContext(); - AbstractOpenObject entity = (AbstractOpenObject) _session.get(titleProperty + ".open"); + AbstractOpenObject entity = (AbstractOpenObject) _session + .get(titleProperty + ".open"); if (entity != null) { getTitleBarSettings().setProgressState( @@ -683,9 +688,10 @@ public class Action extends ActionSupport implements ServletRequestAware, public void setLeftMenuSettings(final LeftMenuSettings leftMenuSettings) { _leftMenuSettings = leftMenuSettings; } - + /** * Get the actionType. + * * @return the actionType */ public String getActionType() { @@ -694,7 +700,9 @@ public class Action extends ActionSupport implements ServletRequestAware, /** * Set the actionType. - * @param actionType the actionType to set + * + * @param actionType + * the actionType to set */ public void setActionType(final String actionType) { _actionType = actionType; @@ -702,6 +710,7 @@ public class Action extends ActionSupport implements ServletRequestAware, /** * Get the message. + * * @return the message */ public String getMessage() { @@ -710,7 +719,9 @@ public class Action extends ActionSupport implements ServletRequestAware, /** * Set the message. - * @param message the message to set + * + * @param message + * the message to set */ public void setMessage(final String message) { _message = message; diff --git a/Workspace/Siman/src/org/splat/simer/ApplicationSettings.java b/Workspace/Siman/src/org/splat/simer/ApplicationSettings.java index cd4e68e..7f9e308 100644 --- a/Workspace/Siman/src/org/splat/simer/ApplicationSettings.java +++ b/Workspace/Siman/src/org/splat/simer/ApplicationSettings.java @@ -53,40 +53,40 @@ public class ApplicationSettings { /** * JNDI context for launching converters. */ - private transient static final Properties _jndprops; + private transient static final Properties _JNDPROPS; /** * Siman web application name. */ - private transient static String _wappname; + private transient static String wappname; /** * General properties from the application properties files. */ - private transient static final Properties _wapprops = new Properties(); + private transient static final Properties _WAPPROPS = new Properties(); /** * Siman web application root path on the server. */ - private transient static String _wapproot; + private transient static String wapproot; /** * Available template files. */ - private transient static Map _tempfile; + private transient static Map tempfile; /** * List of file extensions mapped to a viewer. */ - private transient static String[] _viewermap; + private transient static String[] viewermap; /** * Available document format converters. */ - private transient static Map _convertmap; + private transient static Map convertmap; static { - synchronized (_wapprops) { + synchronized (_WAPPROPS) { // Do common configuration for all users - _jndprops = new Properties(); - _tempfile = new HashMap(); - _viewermap = new String[0]; - _convertmap = new HashMap(); + _JNDPROPS = new Properties(); + tempfile = new HashMap(); + viewermap = new String[0]; + convertmap = new HashMap(); ClassLoader cloader = Thread.currentThread() .getContextClassLoader(); @@ -94,15 +94,15 @@ public class ApplicationSettings { .currentRequestAttributes()).getRequest(); String appname = curRequest.getContextPath(); if (appname.startsWith("/")) { - _wappname = appname.substring(1); + wappname = appname.substring(1); } // Set local path on the server to the application root. - _wapproot = curRequest.getSession().getServletContext() - .getRealPath("/"); + wapproot = curRequest.getSession().getServletContext().getRealPath( + "/"); try { - _jndprops.load(cloader.getResourceAsStream("jndi.properties")); - _wapprops.load(cloader.getResourceAsStream(_wappname + _JNDPROPS.load(cloader.getResourceAsStream("jndi.properties")); + _WAPPROPS.load(cloader.getResourceAsStream(wappname + ".properties")); } catch (IOException e) { LOG.info( @@ -111,11 +111,11 @@ public class ApplicationSettings { } // Configure login security - System.setProperty("java.security.auth.login.config", _wapproot + System.setProperty("java.security.auth.login.config", wapproot + ApplicationSettings.getApplicationProperty("wapp.login")); // Customization (must be done after above default settings) - File config = new File(_wapproot + File config = new File(wapproot + getApplicationProperty("wapp.customization")); if (config.exists()) { loadCustomization(config); // Sets default document types, installed modules and available templates @@ -228,11 +228,15 @@ public class ApplicationSettings { /** * Rename menu item name. */ - private static final String MNU_NAME_RENAME = "menu.rename"; + private static final String MNU_NAME_RENAME = "menu.rename"; /** - * Publish menu item name. + * Mark as reference menu item name. + */ + private static final String MNU_MARK_AS_REFERENCE = "markasreference"; + /** + * Mark as reference menu item label key. */ - private static final String MNU_MARK_AS_REFERENCE = "menu.markasreference"; + private static final String MNU_NAME_MARK_AS_REFERENCE = "menu.markasreference"; /** * Not yet implemented action name. */ @@ -289,7 +293,7 @@ public class ApplicationSettings { + curRequest.getServerPort(); LOG.info("Application server is set to " + _wappserver); - LOG.info("Application name is set to " + _wappname); + LOG.info("Application name is set to " + wappname); } /** @@ -319,10 +323,10 @@ public class ApplicationSettings { "select?menu=create&item=new-empty"); addItem("new-copy", new MenuItem("menu.new.copy") .icon("image.copy.png")); - /*addItem("new-instance", new MenuItem("menu.new.instance") - .icon(IMG_HOLD)); - addItem("new-import", new MenuItem("menu.new.import") - .icon("icon.upload.png"));*/ + /* + * addItem("new-instance", new MenuItem("menu.new.instance") .icon(IMG_HOLD)); addItem("new-import", new + * MenuItem("menu.new.import") .icon("icon.upload.png")); + */ this.selects("new-empty"); } } @@ -344,9 +348,9 @@ public class ApplicationSettings { private PropertiesMenu() { super("configuration"); addItem("prop-general", "menu.prop.general", IMG_HOLD, - "select?menu=properties&item=prop-general"); + "select?menu=configuration&item=prop-general"); addItem("prop-scenario", "menu.prop.scenario", IMG_HOLD, - "select?menu=properties&item=prop-scenario"); + "select?menu=configuration&item=prop-scenario"); // These menu items will not be implemented in the current version. /* * addItem("prop-timestamp", new MenuItem("menu.prop.timestamp") .icon("image.stamp.png")); addItem("prop-comlog", new @@ -379,8 +383,74 @@ public class ApplicationSettings { } } + /** + * Menu items enumeration. + */ private enum Item { - publish, accept, approve, promote, demote, undo, rename, attach, edit, script, version, replace, export, remove, purge + /** + * Publish the study. + */ + publish, + /** + * Accept the document. + */ + accept, + /** + * Approve the document. + */ + approve, + /** + * Promote the document. + */ + promote, + /** + * Demote the docuemnt. + */ + demote, + /** + * Undo the last operation. + */ + undo, + /** + * Rename the document. + */ + rename, + /** + * Attach a file to the document. + */ + attach, + /** + * Edit the document. + */ + edit, + /** + * script + */ + script, + /** + * Version the document. + */ + version, + /** + * replace + */ + replace, + /** + * export + */ + export, + /** + * Remove the document. + */ + remove, + /** + * purge + */ + purge, + /** + * Mark the study as reference. + */ + markasreference }; // Resources relative to studies @@ -389,31 +459,35 @@ public class ApplicationSettings { private EditableStudyPopup() { super(); - addItem(MNU_MARK_AS_REFERENCE, new PopupItem(MNU_MARK_AS_REFERENCE) - .action(ACT_NOT_YET_IMPLEMENTED) - .confirmation("message.markasreference.study")); + addItem(MNU_MARK_AS_REFERENCE, new PopupItem(MNU_NAME_MARK_AS_REFERENCE) + .action(ACT_NOT_YET_IMPLEMENTED).confirmation( + "message.markasreference.study")); addItem(MNU_PUBLISH, new PopupItem(MNU_NAME_PUBLISH).icon( "image.publish.png").action("edit-study?action=publish") .confirmation("message.publish.study")); - /*addItem(MNU_PROMOTE, new PopupItem("menu.archive"));*/ + /* addItem(MNU_PROMOTE, new PopupItem("menu.archive")); */ addSeparator(); addItem(MNU_EDIT, new PopupItem("menu.properties").icon( "icon.ed.png").action("../select?menu=properties")); addSeparator(); addItem(MNU_SCRIPT, new PopupItem(MNU_NAME_SCRIPT) .action("add-scenario")); - /*addItem(MNU_VERSION, new PopupItem(MNU_NAME_VERSION).icon( - IMG_VERSION).action(ACT_NOT_YET_IMPLEMENTED));*/ + /* + * addItem(MNU_VERSION, new PopupItem(MNU_NAME_VERSION).icon( IMG_VERSION).action(ACT_NOT_YET_IMPLEMENTED)); + */ addSeparator(); - /*addItem(MNU_PURGE, new PopupItem(MNU_NAME_PURGE) - .confirmation("message.purge.study")); - addItem("export", new PopupItem("menu.export") - .icon("image.export.png")); // For future needs -*/ addItem(MNU_REMOVE, new PopupItem(MNU_NAME_REMOVE).icon(IMG_DELETE) - .action(ACT_NOT_YET_IMPLEMENTED).confirmation( - "message.delete.study")); + /* + * addItem(MNU_PURGE, new PopupItem(MNU_NAME_PURGE) .confirmation("message.purge.study")); addItem("export", new + * PopupItem("menu.export") .icon("image.export.png")); // For future needs + */addItem(MNU_REMOVE, new PopupItem(MNU_NAME_REMOVE).icon( + IMG_DELETE).action(ACT_NOT_YET_IMPLEMENTED).confirmation( + "message.delete.study")); } + /** + * {@inheritDoc} + * @see org.splat.wapp.ContextualMenu#isEnabled(java.lang.String) + */ @Override public boolean isEnabled(final String name) { boolean res = (_user != null); @@ -688,7 +762,7 @@ public class ApplicationSettings { */ private static class ApprovedPopup extends PopupMenu { private ApprovedPopup() { - super(); + super(); addItem(MNU_ATTACH, new PopupItem(MNU_NAME_ATTACH).icon(IMG_ATTACH) .action(ACT_ATTACH)); addSeparator(); @@ -731,7 +805,7 @@ public class ApplicationSettings { break; case version: res = _user.canVersion(); - break; + break; case replace: res = _user.canReplace(); break; @@ -939,7 +1013,7 @@ public class ApplicationSettings { if (dtype != null) { docname = dtype.getName(); } - if (_tempfile.get(docname) == null) { // No available template + if (tempfile.get(docname) == null) { // No available template String tool = parsed[parsed.length - 1]; String icon = name[0]; if ("index".equals(icon)) { @@ -974,17 +1048,17 @@ public class ApplicationSettings { } public static String getApplicationProperty(final String name) { - return _wapprops.getProperty(name); // May be null + return _WAPPROPS.getProperty(name); // May be null } public static String getApplicationRootPath() { // The property is supposed including the Web application name - return _wapproot; + return wapproot; } public String getApplicationURL() { StringBuffer url = new StringBuffer("http://").append(_wappserver) - .append("/").append(_wappname); + .append("/").append(wappname); return url.toString(); } @@ -997,7 +1071,7 @@ public class ApplicationSettings { } static public Properties getNamingProperties() { - return _jndprops; + return _JNDPROPS; } // ============================================================================================================================== @@ -1018,7 +1092,7 @@ public class ApplicationSettings { public static Converter getConverter(final DocumentType type, final String format) { - return _convertmap.get(format + type.getName()); // May be null; + return convertmap.get(format + type.getName()); // May be null; } public DocumentType getDefaultDocumentType(final Step step, @@ -1052,7 +1126,7 @@ public class ApplicationSettings { } public static Locale[] getSupportedLocales() { - String[] code = _wapprops.getProperty("locale.supported").split(","); + String[] code = _WAPPROPS.getProperty("locale.supported").split(","); Locale[] result = new Locale[code.length]; for (int i = 0; i < code.length; i++) { result[i] = new Locale(code[i]); @@ -1061,7 +1135,7 @@ public class ApplicationSettings { } public static String[] getViewersMapping() { - return _viewermap; + return viewermap; } public static String getWebSiteURL() { @@ -1123,11 +1197,11 @@ public class ApplicationSettings { NamedNodeMap natr = child.getAttributes(); String dext = natr.getNamedItem("extension").getNodeValue(); String exec = natr.getNamedItem("executable").getNodeValue(); - _wapprops.put("executable." + dext, exec); + _WAPPROPS.put("executable." + dext, exec); } // Viewer mappings tag child = children.get("viewers"); - _viewermap = child.getAttributes().getNamedItem("extension") + viewermap = child.getAttributes().getNamedItem("extension") .getNodeValue().split(","); } @@ -1148,7 +1222,7 @@ public class ApplicationSettings { String from = natr.getNamedItem("from").getNodeValue(); String to = natr.getNamedItem("to").getNodeValue(); String exec = natr.getNamedItem("executable").getNodeValue(); - _convertmap.put(from + "geometry", new Converter("geometry", + convertmap.put(from + "geometry", new Converter("geometry", from, to, exec)); } } @@ -1172,7 +1246,7 @@ public class ApplicationSettings { NamedNodeMap natr = child.getAttributes(); String type = natr.getNamedItem("type").getNodeValue(); String file = natr.getNamedItem("file").getNodeValue(); - _tempfile.put(type, file); + tempfile.put(type, file); } } diff --git a/Workspace/Siman/src/org/splat/simer/ConnectionAction.java b/Workspace/Siman/src/org/splat/simer/ConnectionAction.java index 42020b5..4f05316 100644 --- a/Workspace/Siman/src/org/splat/simer/ConnectionAction.java +++ b/Workspace/Siman/src/org/splat/simer/ConnectionAction.java @@ -126,7 +126,7 @@ public class ConnectionAction extends Action { initializationScreenContext(Constants.NONE); res = _backmenu; - if (res == null || "null".equals(res)) { + if (res == null || "null".equals(res) || res.isEmpty()) { res = Constants.NONE; } } catch (FailedLoginException error) { diff --git a/Workspace/Siman/src/org/splat/simer/Converter.java b/Workspace/Siman/src/org/splat/simer/Converter.java index af2237d..816b40a 100644 --- a/Workspace/Siman/src/org/splat/simer/Converter.java +++ b/Workspace/Siman/src/org/splat/simer/Converter.java @@ -4,6 +4,7 @@ import java.io.File; import javax.jms.Connection; import javax.jms.ConnectionFactory; +import javax.jms.JMSException; import javax.jms.Message; import javax.jms.MessageConsumer; import javax.jms.MessageListener; @@ -17,90 +18,108 @@ import org.splat.dal.bo.som.Document; import org.splat.dal.bo.som.Publication; import org.splat.kernel.MismatchException; - public class Converter implements MessageListener { /** * Converter logger. */ - final static private Logger LOG = Logger.getLogger(Converter.class); - - private final String type; // Type of document to be converted (e.g. geometry, model) - private final String from; // Source format (e.g. py, sldprt) - private final String to; // Target format (e.g. brep) - private final String exec; // Command line launching the actual converter - private String fname; // Absolute path of the source file to be converted - -// ============================================================================================================================== -// Constructor -// ============================================================================================================================== - - protected Converter (final String type, final String from, final String to, final String exec) { -// ---------------------------------------------------------------------- - this.type = type; - this.from = from; - this.to = to; - this.exec = exec; - this.fname = null; - - } - -// ============================================================================================================================== -// Public member functions -// ============================================================================================================================== - - public void converts (final Publication source) throws MismatchException { -// ----------------------------------------- - Document sdoc = source.value(); - - if (!sdoc.getType().getName().equals(type)) { - throw new MismatchException(); - } - if (!sdoc.getFormat().equals(from)) { - throw new MismatchException(); + final static private Logger LOG = Logger.getLogger(Converter.class); + + private final String _type; // Type of document to be converted (e.g. geometry, model) + private final String _from; // Source format (e.g. py, sldprt) + private final String _to; // Target format (e.g. brep) + private final String _exec; // Command line launching the actual converter + private String _fname; // Absolute path of the source file to be converted + + // ============================================================================================================================== + // Constructor + // ============================================================================================================================== + + protected Converter(final String type, final String from, final String to, + final String exec) { + // ---------------------------------------------------------------------- + this._type = type; + this._from = from; + this._to = to; + this._exec = exec; + this._fname = null; + } - try { -// Initialization of the asynchronous communication with the actual converter - Context context = new InitialContext(ApplicationSettings.getNamingProperties()); - ConnectionFactory factory = (javax.jms.QueueConnectionFactory)context.lookup("QueueConnectionFactory"); - Connection connex = factory.createConnection(); - Session session = connex.createSession(false, Session.AUTO_ACKNOWLEDGE); - Queue queue = session.createQueue("conversion"); - MessageConsumer consum = session.createConsumer(queue); - -// Listen for arriving messages - consum.setMessageListener(this); - connex.start(); - -// Start the conversion - String command = ApplicationSettings.getApplicationPluginPath() + "converter.jar"; - String option = "-Dresource.dir=\"" + ApplicationSettings.getApplicationResourcePath() + "\""; - File executable = new File(command); - if (!executable.exists()) { - throw new NoSuchMethodException(); + + // ============================================================================================================================== + // Public member functions + // ============================================================================================================================== + + public void converts(final Publication source) throws MismatchException { + // ----------------------------------------- + Document sdoc = source.value(); + + if (!sdoc.getType().getName().equals(_type)) { + throw new MismatchException(); } + if (!sdoc.getFormat().equals(_from)) { + throw new MismatchException(); + } + Connection connex = null; + try { + // Initialization of the asynchronous communication with the actual converter + Context context = new InitialContext(ApplicationSettings + .getNamingProperties()); + ConnectionFactory factory = (javax.jms.QueueConnectionFactory) context + .lookup("QueueConnectionFactory"); + connex = factory.createConnection(); + Session session = connex.createSession(false, + Session.AUTO_ACKNOWLEDGE); + Queue queue = session.createQueue("conversion"); + MessageConsumer consum = session.createConsumer(queue); + + // Listen for arriving messages + consum.setMessageListener(this); + connex.start(); + + // Start the conversion + String command = ApplicationSettings.getApplicationPluginPath() + + "converter.jar"; + String option = "-Dresource.dir=\"" + + ApplicationSettings.getApplicationResourcePath() + "\""; + File executable = new File(command); + if (!executable.exists()) { + throw new NoSuchMethodException(); + } + + File sfile = sdoc.getSourceFile().asFile(); + String args; + + _fname = sfile.getAbsolutePath(); + args = "\"" + _exec + "\" \"" + _fname + "\""; + if (LOG.isInfoEnabled()) { + LOG.info("Launching the conversion of " + sfile.getName() + + " to " + _to.toUpperCase() + " format using " + + command); + } + Runtime.getRuntime().exec( + "\"C:/Program Files/Java/jre6/bin/java.exe\" -jar " + + option + " \"" + command + "\" " + args); + } catch (Exception error) { + LOG.error("Reason: ", error); + } finally { + if (connex != null) { + try { + connex.close(); + } catch (JMSException e) { + LOG.error("Can't close the connection: ", e); + } + } + } + + } + + // ============================================================================================================================== + // Messages + // ============================================================================================================================== - File sfile = sdoc.getSourceFile().asFile(); - String args; - - fname = sfile.getAbsolutePath(); - args = "\"" + exec + "\" \"" + fname + "\""; - if (LOG.isInfoEnabled()) { - LOG.info("Launching the conversion of " + sfile.getName() + " to " + to.toUpperCase() + " format using " + command); - } - Runtime.getRuntime().exec("\"C:/Program Files/Java/jre6/bin/java.exe\" -jar " + option + " \"" + command + "\" " + args); - } - catch (Exception error) { - LOG.error("Reason: ", error); - } - } - -// ============================================================================================================================== -// Messages -// ============================================================================================================================== - - public void onMessage (final Message msg) { - String result = msg.toString(); - LOG.info("Notification of availability of " + result); + public void onMessage(final Message msg) { + String result = msg.toString(); + LOG.info("Notification of availability of " + result); } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/EditDocumentAction.java b/Workspace/Siman/src/org/splat/simer/EditDocumentAction.java index b2197aa..c4260d8 100644 --- a/Workspace/Siman/src/org/splat/simer/EditDocumentAction.java +++ b/Workspace/Siman/src/org/splat/simer/EditDocumentAction.java @@ -140,21 +140,14 @@ public class EditDocumentAction extends DisplayStudyStepAction { public String doDeleteDocument() { setMenu(); + _openStudy = getOpenStudy(); + Step step = _openStudy.getSelectedStep(); + Publication doctag = step.getDocument(Integer.valueOf(_index)); - try { - _openStudy = getOpenStudy(); - - Step step = _openStudy.getSelectedStep(); - Publication doctag = step.getDocument(Integer.valueOf(_index)); - - getStepService().removeDocument(step, doctag); // Updates the data structure - + if (getStepService().removeDocument(step, Long.valueOf(_index))) { // Updates the data structure _openStudy.remove(doctag); // Updates the presentation - return SUCCESS; - } catch (RuntimeException saverror) { - LOG.error("Reason:", saverror); - return ERROR; } + return SUCCESS; } // ============================================================================================================================== diff --git a/Workspace/Siman/src/org/splat/simer/EditStudyAction.java b/Workspace/Siman/src/org/splat/simer/EditStudyAction.java index 577b185..bf4804c 100644 --- a/Workspace/Siman/src/org/splat/simer/EditStudyAction.java +++ b/Workspace/Siman/src/org/splat/simer/EditStudyAction.java @@ -18,26 +18,20 @@ public class EditStudyAction extends DisplayStudyStepAction { // ============================================================================================================================== public String doEdition() { - try { - _openStudy = getOpenStudy(); - - Execute todo = Execute.valueOf(_action); - Study study = _openStudy.getStudyObject(); - - if (todo == Execute.publish) { - getStudyService().moveToPublic(study); - } else if (todo == Execute.promote) { - getStudyService().moveToReference(study); - } - _openStudy.getPopup().setContext("study", - _openStudy.getStudyRights()); // The context has changed - - setMenu(); - - return SUCCESS; - } catch (RuntimeException saverror) { - LOG.error("Reason:", saverror); - return ERROR; + _openStudy = getOpenStudy(); + + Execute todo = Execute.valueOf(_action); + Study study = _openStudy.getStudyObject(); + + if (todo == Execute.publish) { + getStudyService().moveToPublic(study); + } else if (todo == Execute.promote) { + getStudyService().moveToReference(study); } + _openStudy.getPopup().setContext("study", _openStudy.getStudyRights()); // The context has changed + + setMenu(); + + return SUCCESS; } } \ No newline at end of file diff --git a/Workspace/Siman/src/org/splat/simer/ExceptionAction.java b/Workspace/Siman/src/org/splat/simer/ExceptionAction.java new file mode 100644 index 0000000..ce18106 --- /dev/null +++ b/Workspace/Siman/src/org/splat/simer/ExceptionAction.java @@ -0,0 +1,217 @@ +/***************************************************************************** + * Company OPEN CASCADE + * Application SIMAN + * File $Id$ + * Creation date 13.12.2012 + * @author $Author$ + * @version $Revision$ + * @copyright OPEN CASCADE 2012 + *****************************************************************************/ + +package org.splat.simer; + +import java.util.HashSet; +import java.util.Set; + +import org.apache.struts2.ServletActionContext; +import org.splat.exception.BusinessException; +import org.splat.i18n.I18nUtils; +import org.splat.log.AppLogger; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ActionSupport; +import com.opensymphony.xwork2.util.ValueStack; + +/** + * The action for exception processing. + * + * @author Roman Kozlov (RKV) + */ +public class ExceptionAction extends ActionSupport { + + /** + * The serial id. + */ + private static final long serialVersionUID = 4818006367988105560L; + + /** + * The action logger. + */ + private final static AppLogger LOG = AppLogger + .getLogger(ExceptionAction.class); + + /** + * User interface message. + */ + protected String _message; + + /** + * the unexpected error i18n key. + */ + private final static String UNEXPECTED_ERROR_KEY = "message.error.internal"; + + /** + * The name of the param in the http header. + */ + private final static String PARAM_X_REQUESTED_WITH = "x-requested-with"; + + /** + * The value of a ajax request. + */ + private final static String VALUE_AJAX_REQUEST = "XMLHttpRequest"; + + /** + * Process the exception. + *
      + *
    • log the exception
    • + *
    • display an unexpected error screen. This is configured in the struts config file
    • + *
    + * + * @return the struts mapping + */ + public String doProcess() { + ValueStack valueStack = null; + try { + valueStack = ActionContext.getContext().getValueStack(); + Object obj = valueStack.findValue("exception"); + if (obj instanceof BusinessException) { + BusinessException bex = (BusinessException) obj; + LOG.error(bex.getMessageKey(), bex, bex.getContext()); + + try { + this._message = I18nUtils.getMessage(ActionContext + .getContext().getLocale(), bex.getMessageKey(), bex + .getContext()); + } catch (RuntimeException e) { + this._message = e.getMessage(); + } + + }/* + * else if (obj instanceof TechnicalException) { TechnicalException tex = (TechnicalException) obj; + * LOG.error(tex.getMessageKey(), tex, tex.getContext()); + * + * try { this._uiMessage = I18nUtils.getMessage(ActionContext.getContext().getLocale(),tex.getMessageKey(), + * tex.getContext()); } catch (RuntimeException e) { this._uiMessage = e.getMessage(); } } else if (obj instanceof + * DAORequestTimeoutException) { this._uiMessage = + * I18nUtils.getMessage(ActionContext.getContext().getLocale(),"err.a.request.timeout"); LOG.errorMsg(this._uiMessage, + * (Exception)obj); } + */ + else { + + Exception exc = (Exception) obj; + StringBuffer msg = new StringBuffer(); + if (exc.getMessage() != null) { + msg.append(exc.getMessage()); + LOG.errorMsg(exc.getMessage(), exc); + } + Throwable cause = exc.getCause(); + if (null != cause) { + LOG.errorMsg(cause.getMessage(), (Exception) cause); + msg.append(" ").append(cause.getMessage()); + } + try { + this._message = getText(ExceptionAction.UNEXPECTED_ERROR_KEY); + } catch (RuntimeException e) { + this._message = e.getMessage(); + } + if (LOG.isDebugEnabled()) { + this._message += " " + exc.getClass().getSimpleName() + ": " + msg.toString(); + LOG.debug("ExceptionAction: " + exc.getMessage(), exc); + } + } + } catch (RuntimeException e) { + LOG.errorMsg(e.getMessage(), e); + this._message = e.getMessage(); + Throwable cause = e.getCause(); + if (null != cause) { + LOG.errorMsg(cause.getMessage(), (Exception) cause); + this._message = cause.getMessage(); + } + } + + return getResult(valueStack); + } + + /** + * Define the result name: error for ajax request and sub-action, otherwise return success. + * + * @param valueStack + * struts context value stack + * @return action result name + */ + protected String getResult(final ValueStack valueStack) { + String result; + // If subaction or ajaxRequest + if (checkIfSubAction(valueStack) + || (VALUE_AJAX_REQUEST.equalsIgnoreCase(ServletActionContext + .getRequest().getHeader(PARAM_X_REQUESTED_WITH)))) { + result = ERROR; + } else { + result = SUCCESS; + } + return result; + } + + /** + * Check if the exception is raised inside sub action. + * + * @param valueStack + * the action value stack + * @return true if the exception is raised inside sub action, return false otherwise + */ + private boolean checkIfSubAction(final ValueStack valueStack) { + Set actionSet = new HashSet(); + + if (valueStack != null && valueStack.getContext() != null) { + // checks if the action is a chainActionResult. In that case, return false + Object test = valueStack.getContext().get("CHAIN_HISTORY"); + + if (test == null) { + int nValueStackSize = valueStack.size(); + for (int i = 0; i < nValueStackSize; i++) { + String key = "[" + i + "]"; + Object obj = valueStack.findValue(key); + if (obj instanceof ActionSupport + && !(obj instanceof ExceptionAction)) { + actionSet.add((ActionSupport) obj); + } + + if (obj instanceof com.opensymphony.xwork2.util.CompoundRoot) { + com.opensymphony.xwork2.util.CompoundRoot root = (com.opensymphony.xwork2.util.CompoundRoot) obj; + int nSize = root.size(); + for (int j = 0; j < nSize; j++) { + obj = root.get(j); + if ((obj instanceof ActionSupport) + && !(obj instanceof ExceptionAction)) { + actionSet.add((ActionSupport) obj); + } + } + } + } + } else { + actionSet.clear(); + } + } + return (actionSet.size() > 1); + } + + /** + * Get the ui message. + * + * @return the message + */ + public String getMessage() { + return _message; + } + + /** + * Set the user interface message. + * + * @param aMessage + * the user interface message + */ + public void setMessage(final String aMessage) { + _message = aMessage; + } + +} diff --git a/Workspace/Siman/src/org/splat/simer/StudyPropertiesAction.java b/Workspace/Siman/src/org/splat/simer/StudyPropertiesAction.java index be785bc..3462674 100644 --- a/Workspace/Siman/src/org/splat/simer/StudyPropertiesAction.java +++ b/Workspace/Siman/src/org/splat/simer/StudyPropertiesAction.java @@ -284,7 +284,8 @@ public class StudyPropertiesAction extends DisplayStudyStepAction { try { getStudyService().update(study, sprop.setTitle(_stitle)); } catch (InvalidPropertyException e) { - // TODO + // TODO: + LOG.error(e.getMessage(), e); } } else if (_tosave == Save.contributor) { diff --git a/Workspace/Siman/src/spring/applicationContext.xml b/Workspace/Siman/src/spring/applicationContext.xml index 6867411..1225c73 100644 --- a/Workspace/Siman/src/spring/applicationContext.xml +++ b/Workspace/Siman/src/spring/applicationContext.xml @@ -1,6 +1,7 @@ - + - conf/log-messages + conf/log-messages - + + + @@ -53,12 +58,13 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd"> - + - + @@ -66,14 +72,17 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd"> - + - + - - + + @@ -87,8 +96,8 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd"> parent="openObject" scope="session"> - + @@ -102,13 +111,16 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd"> - + - - + + @@ -118,8 +130,9 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd"> - + @@ -128,15 +141,17 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd"> - + - + @@ -145,37 +160,46 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd"> scope="prototype" parent="displayStudyStepAction"> - - + + - + - - + + - + - - - + + + - + @@ -183,37 +207,45 @@ http://www.springframework.org/schema/context/spring-context-3.0.xsd"> scope="prototype" parent="baseAction"> - + - + - + - + - + - - + + - + - - + + - + - + - + - + - - + - + - - + - + diff --git a/Workspace/Siman/src/struts.xml b/Workspace/Siman/src/struts.xml index bc90fe8..d60b1b9 100644 --- a/Workspace/Siman/src/struts.xml +++ b/Workspace/Siman/src/struts.xml @@ -6,359 +6,609 @@ - - - - - - - - - - - - - - - - - - - page.welcome - page.exception - - - page.home - select?menu=search - study/step-study - study/step-knowledge - - page.login - page.home - - - page.home - page.home - select?menu=search - study/step-study - study/step-knowledge - page.home - page.home - - - study/new-empty - study/search-study - study/search-knowledge - study/search-document - study/prop-study - study/prop-scenario - sadmin/indexing - sadmin/select-file?nextAction=importuser - sadmin/scontext - sadmin/knowlelm - - page.home - - - - - - - - - - - - page.error.study - - - - /study/jsonCheckoutRes.jsp - - - - - page.newstudy - - - open-study?selection=0.1 - page.newstudy - page.home - - - - - page.searchstudy - page.home - - - page.searchstudy - page.searchstudy - page.searchstudy - search-study - - - - page.searchknowledge - - - page.searchknowledge - page.searchknowledge - page.searchknowledge - search-knowledge - - - - /study/searchDocument.jsp - - - - - page.displaystudy - - - page.displaystudy - - - page.displaystudy - - - page.displaystudy - - - page.home - - - page.displaystudyproperties - page.editstudyproperties - - - - - page.displayknowledge - - - page.displayknowledge - - - page.displayknowledge - - - page.displayknowledge - - - page.home - - - - - page.displaystudy - - - page.newscenario - - - page.editstudyproperties - - - page.editstudyproperties - - - page.editstudyproperties - - - page.newscenario - - - step-study - step-study - page.displaystudy - - - page.editstudyproperties - - - page.editscenarioproperties - - - - - page.displaystudy - - - - - page.newcontext - page.selectcontext - - - page.newcontext - page.setcontext - - - page.displaystudy - page.newcontext - page.displaystudy - - - page.displaystudy - page.setcontext - page.displaystudy - - - page.displaystudy - page.displaystudy - - - - - page.uploadstudy - - - step-study - import-document?fileName=%{fileName} - version-document?index=%{index}&fileName=%{fileName} - attach-document?index=%{index}&fileName=%{fileName} - page.error.study - - - page.importdocument - page.importerror - - - step-study - step-study - page.importerror - - - page.versiondocument - page.importerror - - - step-study - step-study - page.importerror - - - page.displaystudy - - - page.displaystudy - - - page.displaystudy - - - page.displaystudy - page.displaystudy - - - - - - page.displaystudy - - - page.displaystudy - - - page.displaystudy - - - page.displaystudy - page.displaystudy - - - page.displaystudy - - - page.displaystudy - - - - - - - - - - - - - - page.indexstudies - - - ../study/search-study - - - page.uploadsadmin - - - page.home - importuser?fileName=%{fileName} - - - page.displayuser - - - - - /sadmin/approveSContext.jsp - - - /sadmin/approveSContext.jsp - - - /sadmin/approveKnowelm.jsp - - - - - - - - - - - /sgeom/index.jsp - - - /sgeom/index.jsp - - - /sgeom/index.jsp - - - - - - - - - - - - - /smesh/index.jsp - - - /smesh/index.jsp - - - /smesh/index.jsp - - - + + + + + + + + + + + + + + + exceptionAction + + + + + + + + + + + + page.home + page.home + + + + + + + + + + + page.welcome + page.exception + + + page.home + + select?menu=search + + + study/step-study + + + study/step-knowledge + + + page.login + page.home + + + page.home + page.home + + select?menu=search + + + study/step-study + + + study/step-knowledge + + page.home + page.home + + + + study/new-empty + + + study/search-study + + + study/search-knowledge + + + study/search-document + + + study/prop-study + + + study/prop-scenario + + + sadmin/indexing + + + sadmin/select-file?nextAction=importuser + + + sadmin/scontext + + + sadmin/knowlelm + + + page.home + + + + + + + + + + + + + page.error.study + + + + + /study/jsonCheckoutRes.jsp + + + + + page.newstudy + + + + open-study?selection=0.1 + + page.newstudy + page.home + + + + + + page.searchstudy + + page.home + + + + page.searchstudy + + + page.searchstudy + + page.searchstudy + + search-study + + + + + + page.searchknowledge + + + + + page.searchknowledge + + + page.searchknowledge + + + page.searchknowledge + + + search-knowledge + + + + + /study/searchDocument.jsp + + + + + + page.displaystudy + + + + + page.displaystudy + + + + + page.displaystudy + + + + + page.displaystudy + + + + page.home + + + + page.displaystudyproperties + + + page.editstudyproperties + + + + + + + page.displayknowledge + + + + + page.displayknowledge + + + + + page.displayknowledge + + + + + page.displayknowledge + + + + page.home + + + + + + page.displaystudy + + + + + page.newscenario + + + + + page.editstudyproperties + + + + + page.editstudyproperties + + + + + page.editstudyproperties + + + + + page.newscenario + + + + + step-study + + + step-study + + page.displaystudy + + + + page.editstudyproperties + + + + + page.editscenarioproperties + + + + + + + page.displaystudy + + + + + + page.newcontext + + page.selectcontext + + + + page.newcontext + page.setcontext + + + + page.displaystudy + + page.newcontext + page.displaystudy + + + + page.displaystudy + + page.setcontext + page.displaystudy + + + + page.displaystudy + + page.displaystudy + + + + + + page.uploadstudy + + + + + step-study + + + import-document?fileName=%{fileName} + + + version-document?index=%{index}&fileName=%{fileName} + + + attach-document?index=%{index}&fileName=%{fileName} + + + page.error.study + + + + + page.importdocument + + page.importerror + + + + step-study + + + step-study + + page.importerror + + + + page.versiondocument + + page.importerror + + + + step-study + + + step-study + + page.importerror + + + + page.displaystudy + + + + + page.displaystudy + + + + + page.displaystudy + + + + + page.displaystudy + + page.displaystudy + + + + + + + page.displaystudy + + + + + page.displaystudy + + + + + page.displaystudy + + + + + page.displaystudy + + page.displaystudy + + + + page.displaystudy + + + + + page.displaystudy + + + + + + + + + + + + + + + + page.indexstudies + + + + + ../study/search-study + + + + + page.uploadsadmin + + + + page.home + + importuser?fileName=%{fileName} + + + + + page.displayuser + + + + + + /sadmin/approveSContext.jsp + + + /sadmin/approveSContext.jsp + + + /sadmin/approveKnowelm.jsp + + + + + + + /sgeom/index.jsp + + + /sgeom/index.jsp + + + /sgeom/index.jsp + + + + + + + + + /smesh/index.jsp + + + /smesh/index.jsp + + + /smesh/index.jsp + + + \ No newline at end of file -- 2.39.2