1   /*
2    * Copyright 2003 - 2013 The eFaps Team
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   *
16   * Revision:        $Rev$
17   * Last Changed:    $Date$
18   * Last Changed By: $Author$
19   */
20  
21  package org.efaps.db;
22  
23  import java.sql.SQLException;
24  import java.util.ArrayList;
25  import java.util.HashMap;
26  import java.util.HashSet;
27  import java.util.Hashtable;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Map.Entry;
32  import java.util.Set;
33  
34  import org.apache.commons.collections4.iterators.ReverseListIterator;
35  import org.apache.commons.lang3.builder.ToStringBuilder;
36  import org.efaps.admin.access.AccessCache;
37  import org.efaps.admin.access.AccessTypeEnums;
38  import org.efaps.admin.datamodel.Attribute;
39  import org.efaps.admin.datamodel.AttributeType;
40  import org.efaps.admin.datamodel.Dimension.UoM;
41  import org.efaps.admin.datamodel.SQLTable;
42  import org.efaps.admin.datamodel.Type;
43  import org.efaps.admin.event.EventDefinition;
44  import org.efaps.admin.event.EventType;
45  import org.efaps.admin.event.Parameter;
46  import org.efaps.admin.event.Parameter.ParameterValues;
47  import org.efaps.admin.event.Return;
48  import org.efaps.admin.event.Return.ReturnValues;
49  import org.efaps.ci.CIAttribute;
50  import org.efaps.db.transaction.ConnectionResource;
51  import org.efaps.db.wrapper.SQLUpdate;
52  import org.efaps.util.EFapsException;
53  import org.slf4j.Logger;
54  import org.slf4j.LoggerFactory;
55  
56  /**
57   * @author The eFaps Team
58   * @version $Id$
59   */
60  public class Update
61  {
62      /**
63       * Variable to get the Status of this update.
64       */
65      private static final Status STATUSOK = new Status();
66  
67      /**
68       * Logging instance used in this class.
69       */
70      private static final Logger LOG = LoggerFactory.getLogger(Update.class);
71  
72      /**
73       * The instance variable stores the instance for which this update is made.
74       */
75      private Instance instance = null;
76  
77      /**
78       * Mapping of SQL-Table to Attribute and Value.
79       */
80      private final Map<SQLTable, List<Value>> table2values = new Hashtable<SQLTable, List<Value>>();
81  
82      /**
83       * Mapping of attribute to values.
84       */
85      private final Map<Attribute, Value> attr2values = new HashMap<Attribute, Value>();
86  
87      /**
88       * Mapping of attribute to values.
89       */
90      private final Map<Attribute, Value> trigRelevantAttr2values = new HashMap<Attribute, Value>();
91  
92      /**
93       * @param _type     Type to be updated
94       * @param _id       id to be updated
95       * @throws EFapsException on error
96       */
97      public Update(final Type _type,
98                    final String _id)
99          throws EFapsException
100     {
101         this(Instance.get(_type, _id));
102     }
103 
104     /**
105      * @param _type     Type to be updated
106      * @param _id       id to be updated
107      * @throws EFapsException on error
108      */
109     public Update(final Type _type,
110                   final long _id)
111         throws EFapsException
112     {
113         this(Instance.get(_type, _id));
114     }
115 
116     /**
117      * @param _type     Type to be updated
118      * @param _id       id to be updated
119      * @throws EFapsException on error
120      */
121     public Update(final String _type,
122                   final String _id)
123         throws EFapsException
124     {
125         this(Type.get(_type), _id);
126     }
127 
128     /**
129      * @param _oid OID of the instance to be updated.
130      * @throws EFapsException on error
131      */
132     public Update(final String _oid)
133         throws EFapsException
134     {
135         this(Instance.get(_oid));
136     }
137 
138     /**
139      * @param _instance  instance to be updated.
140      * @throws EFapsException on error
141      */
142     public Update(final Instance _instance)
143         throws EFapsException
144     {
145         setInstance(_instance);
146         addAlwaysUpdateAttributes();
147         if (!Update.STATUSOK.getStati().isEmpty()) {
148             Update.STATUSOK.getStati().clear();
149         }
150     }
151 
152     /**
153      * Add all attributes of the type which must be always updated.
154      *  @throws EFapsException on error
155      */
156     protected void addAlwaysUpdateAttributes()
157         throws EFapsException
158     {
159         final Iterator<?> iter = getInstance().getType().getAttributes().entrySet().iterator();
160         while (iter.hasNext()) {
161             final Map.Entry<?, ?> entry = (Map.Entry<?, ?>) iter.next();
162             final Attribute attr = (Attribute) entry.getValue();
163             final AttributeType attrType = attr.getAttributeType();
164             if (attrType.isAlwaysUpdate()) {
165                 addInternal(attr, false, (Object[]) null);
166             }
167         }
168     }
169 
170     /**
171      * The method closes the SQL statement.
172      *
173      * @see #statement
174      * @throws EFapsException on error
175      */
176     public void close()
177         throws EFapsException
178     {
179     }
180 
181     /**
182      * The method gets all events for the given EventType and executes them in
183      * the given order. If no events are defined, nothing is done. The method
184      * return TRUE if a event was found, otherwise FALSE.
185      *
186      * @param _eventtype trigger events to execute
187      * @return true if a trigger was found and executed, otherwise false
188      * @throws EFapsException on error
189      */
190     protected boolean executeEvents(final EventType _eventtype)
191         throws EFapsException
192     {
193         boolean ret = false;
194         final List<EventDefinition> triggers = getInstance().getType().getEvents(_eventtype);
195         if (triggers != null) {
196             final Parameter parameter = new Parameter();
197             parameter.put(ParameterValues.NEW_VALUES, getNewValuesMap());
198             parameter.put(ParameterValues.INSTANCE, getInstance());
199             for (final EventDefinition evenDef : triggers) {
200                 evenDef.execute(parameter);
201             }
202             ret = true;
203         }
204         return ret;
205     }
206 
207     /**
208      * @return the map of new values send to the triggers
209      */
210     protected final Map<Attribute, Object[]> getNewValuesMap()
211     {
212         // convert the map in a more simple map (following existing API)
213         final Map<Attribute, Object[]> ret = new HashMap<Attribute, Object[]>();
214         for (final Entry<Attribute, Value> entry : this.trigRelevantAttr2values.entrySet()) {
215             ret.put(entry.getKey(), entry.getValue().getValues());
216         }
217         return ret;
218     }
219 
220     /**
221      * @param _attr     name of attribute to update
222      * @param _values   attribute values
223      * @throws EFapsException on error
224      * @return Status
225      */
226     public Status add(final String _attr,
227                       final Object... _values)
228         throws EFapsException
229     {
230         final Attribute attr = getInstance().getType().getAttribute(_attr);
231         if (attr == null) {
232             throw new EFapsException(getClass(), "add.UnknownAttributeName", _attr);
233         }
234         return add(attr, _values);
235     }
236 
237     /**
238      * @param _attr     attribute to update
239      * @param _values    attribute value
240      * @throws EFapsException on error
241      * @return Status
242      */
243     public Status add(final Attribute _attr,
244                       final Object... _values)
245         throws EFapsException
246     {
247         return addInternal(_attr, true, _values);
248     }
249 
250     /**
251      * @param _attr     attribute to update
252      * @param _values    attribute value
253      * @throws EFapsException on error
254      * @return Status
255      */
256     public Status add(final CIAttribute _attr,
257                       final Object... _values)
258         throws EFapsException
259     {
260         final Attribute attr = getInstance().getType().getAttribute(_attr.name);
261         if (attr == null) {
262             throw new EFapsException(getClass(), "add.UnknownAttributeName", _attr);
263         }
264         return add(attr, _values);
265     }
266 
267     /**
268      * @param _attr             Attribute to add
269      * @param _value            value to add
270      * @param _triggerRelevant  is the attribute triggerrelevant
271      * @return Status
272      * @throws EFapsException on error
273      */
274     protected Status addInternal(final Attribute _attr,
275                                  final boolean _triggerRelevant,
276                                  final Object... _value)
277         throws EFapsException
278     {
279         Status ret = Update.STATUSOK;
280         if (_attr.hasEvents(EventType.VALIDATE)) {
281             final List<Return> returns = _attr.executeEvents(EventType.VALIDATE, ParameterValues.NEW_VALUES, _value);
282             for (final Return retu : returns) {
283                 if (retu.get(ReturnValues.TRUE) == null) {
284                     ret = new Status(retu.get(ReturnValues.VALUES), _attr, _value);
285                     break;
286                 }
287             }
288         }
289         final Value value = getValue(_attr, _value);
290         validate(getInstance(), value);
291         List<Value> values = this.table2values.get(_attr.getTable());
292         if (values == null) {
293             values = new ArrayList<Value>();
294             this.table2values.put(_attr.getTable(), values);
295         }
296         values.add(value);
297 
298         this.attr2values.put(_attr, value);
299 
300         if (_triggerRelevant) {
301             this.trigRelevantAttr2values.put(_attr, value);
302         }
303         return ret;
304     }
305 
306     /**
307      * @param _instance instance to be validated
308      * @param _value value to be validated
309      * @throws EFapsException if invalid
310      */
311     protected void validate(final Instance _instance,
312                             final Value _value)
313         throws EFapsException
314     {
315         _value.getAttribute().getAttributeType().getDbAttrType()
316                         .valiate4Update(_value.getAttribute(), getInstance(), _value.getValues());
317     }
318 
319     /**
320      * @param _attr     Attribute
321      * @param _value    values to be evaluated
322      * @return value
323      */
324     protected Value getValue(final Attribute _attr,
325                              final Object[] _value)
326     {
327         final List<Object> values = new ArrayList<Object>();
328         if (_value != null) {
329             for (final Object obj : _value) {
330                 final Object object;
331                 if (obj instanceof Instance) {
332                     object = ((Instance) obj).getId();
333                 } else if (obj instanceof org.efaps.admin.datamodel.Status) {
334                     object = ((org.efaps.admin.datamodel.Status) obj).getId();
335                 } else if (obj instanceof UoM) {
336                     object = ((UoM) obj).getId();
337                 } else {
338                     object = obj;
339                 }
340                 values.add(object);
341             }
342         }
343         return new Value(_attr, _value == null ? null : values.toArray());
344     }
345 
346     /**
347      * Getter method for the instance variable {@link #table2values}.
348      *
349      * @return value of instance variable {@link #table2values}
350      */
351     protected Map<SQLTable, List<Value>> getTable2values()
352     {
353         return this.table2values;
354     }
355 
356     /**
357      * @throws EFapsException thrown from {@link #executeWithoutAccessCheck}
358      * @see #executeWithoutAccessCheck
359      */
360     public void execute()
361         throws EFapsException
362     {
363         final Set<Attribute> attributes = new HashSet<Attribute>();
364         for (final Attribute attr : this.attr2values.keySet()) {
365             final AttributeType attrType = attr.getAttributeType();
366             if (!attrType.isAlwaysUpdate()) {
367                 attributes.add(attr);
368             }
369         }
370         AccessCache.registerUpdate(getInstance());
371         final boolean hasAccess = getType().hasAccess(getInstance(), AccessTypeEnums.MODIFY.getAccessType(),
372                         attributes);
373 
374         if (!hasAccess) {
375             throw new EFapsException(getClass(), "execute.NoAccess", Context.getThreadContext().getPerson());
376         }
377         executeWithoutAccessCheck();
378     }
379 
380     /**
381      * Executes the update without checking the access rights (but with
382      * triggers).
383      * <ol>
384      * <li>executes the pre update trigger (if exists)</li>
385      * <li>executes the override trigger (if exists)</li>
386      * <li>executes if no override trigger exists or the override trigger is not
387      * executed the update ({@see #executeWithoutTrigger})</li>
388      * <li>executes the post update trigger (if exists)</li>
389      * </ol>
390      *
391      * @throws EFapsException thrown from {@link #executeWithoutTrigger} or when
392      *             the Status is invalid
393      * @see #executeWithoutTrigger
394      */
395     public void executeWithoutAccessCheck()
396         throws EFapsException
397     {
398         if (Update.STATUSOK.getStati().isEmpty()) {
399 
400             executeEvents(EventType.UPDATE_PRE);
401 
402             if (!executeEvents(EventType.UPDATE_OVERRIDE)) {
403                 executeWithoutTrigger();
404             }
405 
406             executeEvents(EventType.UPDATE_POST);
407         } else {
408             throw new EFapsException(getClass(), "executeWithout.StatusInvalid", Update.STATUSOK.getStati());
409         }
410     }
411 
412     /**
413      * The update is done without calling triggers and check of access rights.
414      *
415      * @throws EFapsException if update not possible (unique key, object does
416      *             not exists, etc...)
417      */
418     public void executeWithoutTrigger()
419         throws EFapsException
420     {
421         if (Update.STATUSOK.getStati().isEmpty()) {
422             final Context context = Context.getThreadContext();
423             ConnectionResource con = null;
424             try {
425                 con = context.getConnectionResource();
426                 for (final Entry<SQLTable, List<Value>> entry : this.table2values.entrySet()) {
427                     final SQLUpdate update = Context.getDbType().newUpdate(entry.getKey().getSqlTable(),
428                                     entry.getKey().getSqlColId(),
429                                     this.instance.getId());
430                     // iterate in reverse order and only execute the ones that are not added yet, permitting
431                     // to overwrite the value for attributes by adding them later
432                     final ReverseListIterator<Value> iterator = new ReverseListIterator<Value>(entry.getValue());
433 
434                     final Set<String> added = new HashSet<String>();
435                     while (iterator.hasNext()) {
436                         final Value value = iterator.next();
437                         final String colKey = value.getAttribute().getSqlColNames().toString();
438                         if (!added.contains(colKey)) {
439                             value.getAttribute().prepareDBUpdate(update, value.getValues());
440                             added.add(colKey);
441                         }
442 
443                     }
444                     update.execute(con.getConnection());
445                 }
446                 con.commit();
447             } catch (final SQLException e) {
448                 Update.LOG.error("Update of '" + this.instance + "' not possible", e);
449                 throw new EFapsException(getClass(), "executeWithoutTrigger.SQLException", e, this.instance);
450             } finally {
451                 if ((con != null) && con.isOpened()) {
452                     con.abort();
453                 }
454             }
455         } else {
456             throw new EFapsException(getClass(), "executeWithout.StatusInvalid", Update.STATUSOK.getStati());
457         }
458     }
459 
460     /**
461      * The instance method returns the Type instance of {@link #instance}.
462      *
463      * @return type of {@link #instance}
464      * @see #instance
465      */
466     protected Type getType()
467     {
468         return getInstance().getType();
469     }
470 
471 
472 
473     /**
474      * The instance method returns the id of {@link #instance}.
475      *
476      * @return id of {@link #instance}
477      * @see #instance
478      */
479     public long getId()
480     {
481         return getInstance().getId();
482     }
483 
484     /**
485      * This is the getter method for instance variable {@link #instance}.
486      *
487      * @return value of instance variable {@link #instance}
488      * @see #instance
489      * @see #setInstance
490      */
491     public Instance getInstance()
492     {
493         return this.instance;
494     }
495 
496     /**
497      * This is the setter method for instance variable {@link #instance}.
498      *
499      * @param _instance new value for instance variable {@link #instance}
500      * @see #instance
501      * @see #getInstance
502      */
503     protected void setInstance(final Instance _instance)
504     {
505         this.instance = _instance;
506     }
507 
508     /**
509      * Class is used to set a satus of the update.
510      */
511     public static class Status
512     {
513 
514         /**
515          * this instance variable is only used in static Status STATUSOK.<br>
516          * It stores all Instances of Status which are not ok.
517          *
518          * @see #getStati()
519          */
520         private final List<Update.Status> stati = new ArrayList<Update.Status>();
521 
522         /**
523          * This instance variable stores the ReturnValue of the esjp.
524          *
525          * @see #getReturnValue()
526          */
527         private final Object returnValue;
528 
529         /**
530          * this instance variable stores the Value wich was thought for the
531          * Attribute.
532          *
533          * @see #getAttribute()
534          */
535         private final Object value;
536 
537         /**
538          * this instance variable stores the Attribute wich led to the creation
539          * of this Status.
540          */
541         private final Attribute attribute;
542 
543         /**
544          * Constructor setting the instance variables and stores this Status in
545          * STATUSOK.
546          *
547          * @param _returnvalue  value to be returned
548          * @param _attribute    attribute
549          * @param _value        value
550          */
551         public Status(final Object _returnvalue,
552                       final Attribute _attribute,
553                       final Object _value)
554         {
555             this.returnValue = _returnvalue;
556             this.value = _value;
557             this.attribute = _attribute;
558             Update.STATUSOK.getStati().add(this);
559         }
560 
561         /**
562          * defualt constructor.
563          */
564         public Status()
565         {
566             this.returnValue = null;
567             this.value = null;
568             this.attribute = null;
569         }
570 
571         /**
572          * This method can be called to see if the Status is Ok.
573          *
574          * @return true if ok, else false
575          */
576         public boolean isOk()
577         {
578             boolean ret = false;
579             if (equals(Update.STATUSOK)) {
580                 ret = true;
581             }
582             return ret;
583         }
584 
585         /**
586          * This is the getter method for the instance variable
587          * {@link #returnValue}.
588          *
589          * @return value of instance variable {@link #returnValue}
590          */
591 
592         public Object getReturnValue()
593         {
594             return this.returnValue;
595         }
596 
597         /**
598          * This is the getter method for the instance variable {@link #value}.
599          *
600          * @return value of instance variable {@link #value}
601          */
602 
603         public Object getValue()
604         {
605             return this.value;
606         }
607 
608         /**
609          * This is the getter method for the instance variable
610          * {@link #attribute}.
611          *
612          * @return value of instance variable {@link #attribute}
613          */
614 
615         public Attribute getAttribute()
616         {
617             return this.attribute;
618         }
619 
620         /**
621          * This is the getter method for the instance variable {@link #stati}.
622          *
623          * @return value of instance variable {@link #stati}
624          */
625 
626         public List<Update.Status> getStati()
627         {
628             return this.stati;
629         }
630 
631         /**
632          * @see java.lang.Object#toString()
633          * @return String representation of this class
634          */
635         @Override
636         public String toString()
637         {
638             return new ToStringBuilder(this).append("AttributeName", getAttribute().getName())
639                 .append(" Value", getValue()).append(" ReturnValue:", getReturnValue()).toString();
640         }
641     }
642 
643     /**
644      * Represents one value for an attribte that will be updated.
645      */
646     protected static final class Value
647     {
648         /**
649          * Attribute the value belongs to.
650          */
651         private final Attribute attribute;
652 
653         /**
654          * Values for the Attribute.
655          */
656         private final Object[] values;
657 
658         /**
659          * @param _attribute    Attribute the value belongs to
660          * @param _values       Valuers for the Attribute
661          */
662         private Value(final Attribute _attribute,
663                       final Object... _values)
664         {
665             this.attribute = _attribute;
666             this.values = _values;
667         }
668 
669         /**
670          * @return the attribute
671          */
672         public Attribute getAttribute()
673         {
674             return this.attribute;
675         }
676 
677         /**
678          * @return the values
679          */
680         public Object[] getValues()
681         {
682             return this.values;
683         }
684 
685         @Override
686         public String toString()
687         {
688             return ToStringBuilder.reflectionToString(this);
689         }
690     }
691 }