1   /*
2    *
3    *  Copyright 2003 - 2013 The eFaps Team
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   *
17   *  Revision:        $Rev$
18   *  Last Changed:    $Date$
19   *  Last Changed By: $Author$
20   */
21  package org.efaps.admin;
22  
23  import java.io.Serializable;
24  import java.sql.PreparedStatement;
25  import java.sql.ResultSet;
26  import java.sql.SQLException;
27  import java.sql.Statement;
28  import java.util.ArrayList;
29  import java.util.Collections;
30  import java.util.Comparator;
31  import java.util.HashMap;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.UUID;
35  
36  import org.apache.commons.collections4.MapUtils;
37  import org.apache.commons.lang3.builder.ToStringBuilder;
38  import org.efaps.admin.datamodel.Type;
39  import org.efaps.admin.event.EventDefinition;
40  import org.efaps.admin.event.EventType;
41  import org.efaps.admin.event.Parameter;
42  import org.efaps.admin.event.Parameter.ParameterValues;
43  import org.efaps.admin.event.Return;
44  import org.efaps.admin.ui.AbstractUserInterfaceObject;
45  import org.efaps.db.Context;
46  import org.efaps.db.transaction.ConnectionResource;
47  import org.efaps.db.wrapper.SQLPart;
48  import org.efaps.db.wrapper.SQLSelect;
49  import org.efaps.util.EFapsException;
50  import org.efaps.util.cache.CacheObjectInterface;
51  import org.efaps.util.cache.CacheReloadException;
52  import org.slf4j.Logger;
53  import org.slf4j.LoggerFactory;
54  
55  /**
56   * @author The eFaps Team
57   * @version $Id$
58   */
59  public abstract class AbstractAdminObject
60      implements CacheObjectInterface, Serializable
61  {
62      /**
63       * Needed for serialization.
64       */
65      private static final long serialVersionUID = 1L;
66  
67      /**
68       * Logging instance used in this class.
69       */
70      private static final Logger LOG = LoggerFactory.getLogger(AbstractAdminObject.class);
71  
72      /**
73       * Select statement to get the properties.
74       */
75      private static final String SELECT = new SQLSelect()
76                      .column("NAME")
77                      .column("VALUE")
78                      .from("T_CMPROPERTY")
79                      .addPart(SQLPart.WHERE)
80                      .addColumnPart(null, "ABSTRACT")
81                      .addPart(SQLPart.EQUAL)
82                      .addValuePart("?").toString();
83  
84      /**
85       * The instance variable stores the id of the collections object.
86       *
87       * @see #getId
88       */
89      private final long id;
90  
91      /**
92       * This is the instance variable for the universal unique identifier of this
93       * admin object.
94       *
95       * @see #getUUID
96       */
97      private final UUID uuid;
98  
99      /**
100      * This is the instance variable for the name of this admin object.
101      *
102      * @see #setName
103      * @see #getName
104      */
105     private final String name;
106 
107     /**
108      * This is the instance variable for the properties.
109      *
110      * @getProperties
111      */
112     private final Map<String, String> properties = new HashMap<String, String>();
113 
114     /**
115      * All events for this AdminObject are stored in this map.
116      */
117     private final Map<EventType, List<EventDefinition>> events = new HashMap<EventType, List<EventDefinition>>();
118 
119     /**
120      * checked for events?
121      */
122     private boolean eventChecked = false;
123 
124     /**
125      * Is this Admin Object dirty, meaning was it altered after first initialization
126      * and it might be necessary to cache it again.
127      */
128     private boolean dirty = false;
129 
130 
131     /**
132      * Constructor to set instance variables {@link #id}, {@link #uuid} and
133      * {@link #name} of this administrational object.
134      *
135      * @param _id id to set
136      * @param _uuid universal unique identifier
137      * @param _name name to set
138      * @see #id
139      * @see #uuid
140      * @see #name
141      */
142     protected AbstractAdminObject(final long _id,
143                                   final String _uuid,
144                                   final String _name)
145     {
146         this.id = _id;
147         this.uuid = _uuid == null || _uuid.trim().isEmpty() ? null : UUID.fromString(_uuid.trim());
148         this.name = _name == null ? null : _name.trim();
149     }
150 
151     /**
152      * Sets the link properties for this object.
153      *
154      * @param _linkTypeUUID UUID of the type of the link property
155      * @param _toId to id
156      * @param _toTypeUUID UUDI of the to type
157      * @param _toName to name
158      * @throws EFapsException on error
159      */
160     protected void setLinkProperty(final UUID _linkTypeUUID,
161                                    final long _toId,
162                                    final UUID _toTypeUUID,
163                                    final String _toName)
164         throws EFapsException
165     {
166         setDirty();
167     }
168 
169     /**
170      * The instance method sets all properties of this administrational object.
171      * All properties are stores in instance variable {@link #properties}.
172      *
173      * @param _name name of the property (key)
174      * @param _value value of the property
175      * @see #properties
176      * @throws CacheReloadException on error
177      */
178     protected void setProperty(final String _name,
179                                final String _value)
180         throws CacheReloadException
181     {
182         getProperties().put(_name, _value);
183         setDirty();
184     }
185 
186     /**
187      * The value of the given property is returned.
188      *
189      * @param _name name of the property (key)
190      * @return value of the property with the given name / key.
191      * @see #properties
192      */
193     public String getProperty(final String _name)
194     {
195         return getProperties().get(_name);
196     }
197 
198     /**
199      * @return unmodifiable map of properties
200      */
201     public Map<String, String> getPropertyMap()
202     {
203         return MapUtils.unmodifiableMap(getProperties());
204     }
205 
206     /**
207      * Adds a new event to this AdminObject.
208      *
209      * @param _eventtype Eventtype class name to add
210      * @param _eventdef EventDefinition to add
211      * @see #events
212      * @throws CacheReloadException on error
213      */
214     public void addEvent(final EventType _eventtype,
215                          final EventDefinition _eventdef)
216         throws CacheReloadException
217     {
218         List<EventDefinition> evenList = this.events.get(_eventtype);
219         if (evenList == null) {
220             evenList = new ArrayList<EventDefinition>();
221             this.events.put(_eventtype, evenList);
222         }
223         if (!evenList.contains(_eventdef)) {
224             evenList.add(_eventdef);
225         }
226         // if there are more than one event the y must be sorted by their index
227         // position
228         if (evenList.size() > 1) {
229             Collections.sort(evenList, new Comparator<EventDefinition>()
230             {
231                 @Override
232                 public int compare(final EventDefinition _eventDef0,
233                                    final EventDefinition _eventDef1)
234                 {
235                     return Long.valueOf(_eventDef0.getIndexPos()).compareTo(Long.valueOf(_eventDef1.getIndexPos()));
236                 }
237             });
238         }
239         setDirty();
240     }
241 
242     /**
243      * Returns the list of events defined for given event type.
244      *
245      * @param _eventType event type
246      * @return list of events for the given event type
247      */
248     public List<EventDefinition> getEvents(final EventType _eventType)
249     {
250         if (!this.eventChecked) {
251             this.eventChecked = true;
252             try {
253                 EventDefinition.addEvents(this);
254             } catch (final EFapsException e) {
255                 AbstractAdminObject.LOG.error("Could not read events for Name:; {}', UUID: {}",  this.name, this.uuid);
256             }
257         }
258         return this.events.get(_eventType);
259     }
260 
261     /**
262      * Does this instance have Event, for the specified EventType ?
263      *
264      * @param _eventtype type of event to check for
265      * @return <i>true</i>, if this instance has a trigger, otherwise
266      *         <i>false</i>.
267      */
268     public boolean hasEvents(final EventType _eventtype)
269     {
270         if (!this.eventChecked) {
271             this.eventChecked = true;
272             try {
273                 EventDefinition.addEvents(this);
274             } catch (final EFapsException e) {
275                 AbstractAdminObject.LOG.error("Could not read events for Name:; {}', UUID: {}",  this.name, this.uuid);
276             }
277         }
278         return this.events.get(_eventtype) != null;
279     }
280 
281     /**
282      * The method gets all events for the given event type and executes them in
283      * the given order. If no events are defined, nothing is done.
284      *
285      * @param _eventtype type of event to execute
286      * @param _args arguments used as parameter (doubles with first parameters
287      *            defining the key, second parameter the value itself)
288      * @return List with Returns
289      * @throws EFapsException on error
290      */
291     public List<Return> executeEvents(final EventType _eventtype,
292                                       final Object... _args)
293         throws EFapsException
294     {
295         final List<Return> ret = new ArrayList<Return>();
296         if (hasEvents(_eventtype)) {
297             final Parameter param = new Parameter();
298             if (_args != null) {
299                 // add all parameters
300                 for (int i = 0; i < _args.length; i += 2) {
301                     if (i + 1 < _args.length && _args[i] instanceof ParameterValues) {
302                         param.put((ParameterValues) _args[i], _args[i + 1]);
303                     }
304                 }
305             }
306             ret.addAll(executeEvents(_eventtype, param));
307         }
308         return ret;
309     }
310 
311     /**
312      * The method gets all events for the given event type and executes them in
313      * the given order. If no events are defined, nothing is done.
314      *
315      * @param _eventtype type of event to execute
316      * @param _param Parameter to be passed to the esjp
317      * @return List with Returns
318      * @throws EFapsException on error
319      */
320     public List<Return> executeEvents(final EventType _eventtype,
321                                       final Parameter _param)
322         throws EFapsException
323     {
324         final List<Return> ret = new ArrayList<Return>();
325         if (hasEvents(_eventtype)) {
326             if (this instanceof AbstractUserInterfaceObject) {
327                 // add ui object to parameter
328                 _param.put(ParameterValues.UIOBJECT, this);
329             }
330             // execute all triggers
331             for (final EventDefinition evenDef : this.events.get(_eventtype)) {
332                 ret.add(evenDef.execute(_param));
333             }
334         }
335         return ret;
336     }
337 
338     /**
339      * The instance method reads the properties for this administration object.
340      * Each found property is set with instance method {@link #setProperty}.
341      *
342      * @throws CacheReloadException on error
343      *
344      * @see #setProperty
345      */
346     protected void readFromDB4Properties()
347         throws CacheReloadException
348     {
349         ConnectionResource con = null;
350         try {
351             con = Context.getThreadContext().getConnectionResource();
352             final PreparedStatement stmt = con.getConnection().prepareStatement(AbstractAdminObject.SELECT);
353             stmt.setObject(1, getId());
354             final ResultSet rs = stmt.executeQuery();
355             AbstractAdminObject.LOG.debug("Reading Properties for '{}'", getName());
356             while (rs.next()) {
357                 final String nameStr = rs.getString(1).trim();
358                 final String value = rs.getString(2).trim();
359                 setProperty(nameStr, value);
360                 AbstractAdminObject.LOG.debug("    Name: '{}' - Value: '{}'", new Object[] { nameStr, value });
361             }
362             rs.close();
363             stmt.close();
364             if (con.isOpened()) {
365                 con.commit();
366             }
367         } catch (final SQLException e) {
368             throw new CacheReloadException("could not read properties for " + "'" + getName() + "'", e);
369         } catch (final EFapsException e) {
370             throw new CacheReloadException("could not read properties for " + "'" + getName() + "'", e);
371         } finally {
372             if (con != null && con.isOpened()) {
373                 try {
374                     con.abort();
375                 } catch (final EFapsException e) {
376                     throw new CacheReloadException("could not read properties for " + "'" + getName() + "'", e);
377                 }
378             }
379         }
380     }
381 
382     /**
383      * Reads all links for this administration object. Each found link property
384      * is set with instance method {@link setLinkProperty}.
385      *
386      * @throws CacheReloadException on error
387      * @see #setLinkProperty
388      */
389     protected void readFromDB4Links()
390         throws CacheReloadException
391     {
392         ConnectionResource con = null;
393         try {
394             con = Context.getThreadContext().getConnectionResource();
395             final SQLSelect select = new SQLSelect()
396                             .column(0, "TYPEID")
397                             .column(0, "TOID")
398                             .column(1, "TYPEID")
399                             .column(1, "NAME")
400                             .from("T_CMABSTRACT2ABSTRACT", 0)
401                             .leftJoin("T_CMABSTRACT", 1, "ID", 0, "TOID")
402                             .addPart(SQLPart.WHERE)
403                             .addColumnPart(0, "FROMID")
404                             .addPart(SQLPart.EQUAL)
405                             .addValuePart(getId());
406 
407             final Statement stmt = con.getConnection().createStatement();
408             final ResultSet rs = stmt.executeQuery(select.getSQL());
409 
410             AbstractAdminObject.LOG.debug("Reading Links for '{}'", getName());
411 
412             final List<Object[]> values = new ArrayList<Object[]>();
413             while (rs.next()) {
414                 final long conTypeId = rs.getLong(1);
415                 final long toId = rs.getLong(2);
416                 final long toTypeId = rs.getLong(3);
417                 final String toName = rs.getString(4);
418                 values.add(new Object[] { conTypeId, toId, toTypeId, toName.trim() });
419             }
420             rs.close();
421             stmt.close();
422             con.commit();
423 
424             for (final Object[] row : values) {
425                 final UUID conTypeUUID = Type.getUUID4Id((Long) row[0]);
426                 AbstractAdminObject.LOG.debug("     Connection Type UUID: {}", conTypeUUID);
427                 final UUID toTypeUUID = Type.getUUID4Id((Long) row[2]);
428                 AbstractAdminObject.LOG.debug("     To Type UUID: {}", toTypeUUID);
429                 if (conTypeUUID != null && toTypeUUID != null) {
430                     setLinkProperty(conTypeUUID, (Long) row[1], toTypeUUID, String.valueOf(row[3]));
431                     AbstractAdminObject.LOG.debug("     ID: {}, name: {}", row[1], row[3]);
432                 }
433             }
434         } catch (final SQLException e) {
435             throw new CacheReloadException("could not read db links for " + "'" + getName() + "'", e);
436             // CHECKSTYLE:OFF
437         } catch (final RuntimeException e) {
438             // CHECKSTYLE:ON
439             throw new CacheReloadException("could not read db links for " + "'" + getName() + "'", e);
440         } catch (final EFapsException e) {
441             throw new CacheReloadException("could not read properties for " + "'" + getName() + "'", e);
442         } finally {
443             if (con != null && con.isOpened()) {
444                 try {
445                     con.abort();
446                 } catch (final EFapsException e) {
447                     throw new CacheReloadException("could not read properties for " + "'" + getName() + "'", e);
448                 }
449             }
450         }
451     }
452 
453     /**
454      * This is the getter method for instance variable {@link #id}.
455      *
456      * @return value of instance variable {@id}
457      * @see #id
458      */
459     @Override
460     public long getId()
461     {
462         return this.id;
463     }
464 
465     /**
466      * This is the getter method for instance variable {@link #uuid}.
467      *
468      * @return value of instance variable {@uuid}
469      * @see #uuid
470      */
471     @Override
472     public UUID getUUID()
473     {
474         return this.uuid;
475     }
476 
477     /**
478      * This is the getter method for instance variable {@link #name}.
479      *
480      * @return value of instance variable {@link #name}
481      * @see #name
482      * @see #setName
483      */
484     @Override
485     public String getName()
486     {
487         return this.name;
488     }
489 
490     /**
491      * This is the getter method for instance variable {@link #properties}.
492      *
493      * @return value of instance variable {@link #properties}
494      * @see #properties
495      */
496     protected Map<String, String> getProperties()
497     {
498         return this.properties;
499     }
500 
501     /**
502      * This is the getter method for instance variable {@link #events}.
503      *
504      * @return value of instance variable {@link #events}
505      * @see #events
506      */
507     protected Map<EventType, List<EventDefinition>> getEvents()
508     {
509         return this.events;
510     }
511 
512 
513     /**
514      * Getter method for the instance variable {@link #dirty}.
515      *
516      * @return value of instance variable {@link #dirty}
517      */
518     public boolean isDirty()
519     {
520         return this.dirty;
521     }
522 
523     /**
524      * Declare Object as dirty.
525      */
526     protected void setDirty()
527     {
528         this.dirty = true;
529     }
530 
531     /**
532      * Declare Object as undirty.
533      */
534     protected void setUndirty()
535     {
536         this.dirty = false;
537     }
538     /**
539      * The method overrides the original method 'toString' and returns the name
540      * of the user interface object.
541      *
542      * @return name of the user interface object
543      */
544     @Override
545     public String toString()
546     {
547         return new ToStringBuilder(this).append("name", getName()).append("uuid", getUUID()).append("id", getId())
548                         .append("properties", getProperties()).append("events", this.events).toString();
549     }
550 }