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.io.IOException;
24  import java.io.InputStream;
25  import java.sql.Connection;
26  import java.sql.SQLException;
27  import java.util.HashMap;
28  import java.util.HashSet;
29  import java.util.Locale;
30  import java.util.Map;
31  import java.util.Set;
32  import java.util.Stack;
33  
34  import javax.naming.InitialContext;
35  import javax.naming.NamingException;
36  import javax.sql.DataSource;
37  import javax.transaction.HeuristicMixedException;
38  import javax.transaction.HeuristicRollbackException;
39  import javax.transaction.NotSupportedException;
40  import javax.transaction.RollbackException;
41  import javax.transaction.Status;
42  import javax.transaction.SystemException;
43  import javax.transaction.Transaction;
44  import javax.transaction.TransactionManager;
45  
46  import org.apache.commons.lang.RandomStringUtils;
47  import org.efaps.admin.user.Company;
48  import org.efaps.admin.user.Person;
49  import org.efaps.admin.user.UserAttributesSet;
50  import org.efaps.admin.user.UserAttributesSet.UserAttributesDefinition;
51  import org.efaps.db.databases.AbstractDatabase;
52  import org.efaps.db.databases.DataBaseFactory;
53  import org.efaps.db.store.Resource;
54  import org.efaps.db.store.Store;
55  import org.efaps.db.transaction.ConnectionResource;
56  import org.efaps.init.INamingBinds;
57  import org.efaps.init.IeFapsProperties;
58  import org.efaps.init.StartupException;
59  import org.efaps.util.EFapsException;
60  import org.efaps.util.cache.CacheReloadException;
61  import org.joda.time.Chronology;
62  import org.joda.time.DateTimeZone;
63  import org.slf4j.Logger;
64  import org.slf4j.LoggerFactory;
65  
66  /**
67   * @author The eFaps Team
68   * @version $Id$
69   */
70  public final class Context
71      implements INamingBinds
72  {
73  
74      /**
75       * Key used to access the current company from the userattributes.
76       */
77      public static final String CURRENTCOMPANY = Context.class.getName() + ".CurrentCompany";
78  
79      /**
80       * Logging instance used in this class.
81       */
82      private static final Logger LOG = LoggerFactory.getLogger(Context.class);
83  
84      /**
85       * Static variable storing the database type.
86       */
87      private static AbstractDatabase<?> DBTYPE;
88  
89      /**
90       * SQL data source to the database.
91       */
92      private static DataSource DATASOURCE;
93  
94      /**
95       * Stores the transaction manager.
96       *
97       * @see #setTransactionManager
98       */
99      private static TransactionManager TRANSMANAG;
100 
101     /**
102      * STore the timeout for the transaction manager.
103      */
104     private static int TRANSMANAGTIMEOUT = 0;
105 
106     static {
107         try {
108             final InitialContext initCtx = new InitialContext();
109             javax.naming.Context envCtx = null;
110             try {
111                 envCtx = (javax.naming.Context) initCtx.lookup("java:/comp/env");
112             } catch (final NamingException e) {
113                 Context.LOG.info("Expected NamingException during evaluation for Context, No action required");
114             }
115             // for a build the context might be different, try this before surrender
116             if (envCtx == null) {
117                 envCtx = (javax.naming.Context) initCtx.lookup("java:comp/env");
118             }
119 
120             Context.DATASOURCE = (DataSource) envCtx.lookup(INamingBinds.RESOURCE_DATASOURCE);
121             try {
122                 final AbstractDatabase<?> dbType  = (AbstractDatabase<?>) envCtx.lookup(INamingBinds.RESOURCE_DBTYPE);
123                 Context.DBTYPE = dbType;
124             } catch (final NamingException e) {
125                 Context.LOG.info("Expected NamingException during evaluation for Context, No action required");
126                 Context.DBTYPE = DataBaseFactory.getDatabase(Context.DATASOURCE.getConnection());
127             }
128 
129             Context.TRANSMANAG = (TransactionManager) envCtx.lookup(INamingBinds.RESOURCE_TRANSMANAG);
130 
131             try {
132                 Context.TRANSMANAGTIMEOUT = 0;
133                 final Map<?, ?> props = (Map<?, ?>) envCtx.lookup(INamingBinds.RESOURCE_CONFIGPROPERTIES);
134                 if (props != null) {
135                     final String transactionTimeoutString = (String) props.get(IeFapsProperties.TRANSACTIONTIMEOUT);
136                     if (transactionTimeoutString != null) {
137                         Context.TRANSMANAGTIMEOUT = Integer.parseInt(transactionTimeoutString);
138                     }
139                 }
140             } catch (final NamingException e) {
141                 // this is actual no error, so nothing is presented
142                 Context.TRANSMANAGTIMEOUT = 0;
143             }
144         } catch (final NamingException e) {
145             Context.LOG.error("NamingException", e);
146             throw new Error(e);
147         } catch (final SQLException e) {
148             Context.LOG.error("SQLException", e);
149             throw new Error(e);
150         }
151     }
152 
153     /**
154      * Each thread has his own context object. The value is automatically
155      * assigned from the filter class. This allows to have a different Context
156      * for every Users which is connect to the WebApp Server. For the case that
157      * a thread creates a child threat the context is inherited to this new
158      * thread. This is needed e.g. in JasperReport for SubReports.
159      * @see #inherit
160      */
161     private static ThreadLocal<Context> INHERITTHREADCONTEXT = new InheritableThreadLocal<Context>();
162 
163     /**
164      * Each thread has his own context object. The value is automatically
165      * assigned from the filter class. This allows to have a different Context
166      * for every Users which is connect to the WebApp Server. For the case that
167      * a thread creates a child threat a different context is created this is
168      * needed e.g. for background process form quartz.
169      */
170     private static ThreadLocal<Context> THREADCONTEXT = new ThreadLocal<Context>();
171 
172     /**
173      * The instance variable stores all open instances of {@link Resource}.
174      *
175      * @see #getStoreResource(Instance)
176      * @see #getStoreResource(Type,long)
177      */
178     private final Set<Resource> storeStore = new HashSet<Resource>();
179 
180     /**
181      * Stores all created connection resources.
182      */
183     private final Set<ConnectionResource> connectionStore = new HashSet<ConnectionResource>();
184 
185     /**
186      * Stack used to store returned connections for reuse.
187      */
188     private final Stack<ConnectionResource> connectionStack = new Stack<ConnectionResource>();
189 
190     /**
191      * Transaction for the context.
192      */
193     private Transaction transaction;
194 
195     /**
196      * This is the instance variable for the SQL Connection to the database.
197      *
198      * @see #getConnection
199      * @see #setConnection
200      */
201     private Connection connection = null;
202 
203     /**
204      * This instance variable represents the user of the context.
205      *
206      * @see #getPerson
207      */
208     private Person person = null;
209 
210     /**
211      * The current active company.
212      */
213     private Long companyId = null;
214 
215     /**
216      * The parameters used to open a new thread context are stored in this
217      * instance variable (e.g. the request parameters from a http servlet are
218      * stored in this variable).
219      *
220      * @see #getParameters
221      */
222     private final Map<String, String[]> parameters;
223 
224     /**
225      * The file parameters used to open a new thread context are stored in this
226      * instance variable (e.g. the request parameters from a http servlet or in
227      * the shell the parameters from the command shell). The file item
228      * represents one file which includes an input stream, the name and the
229      * length of the file.
230      *
231      * @see #getFileParameters
232      */
233     private final Map<String, FileParameter> fileParameters;
234 
235     /**
236      * A map to be able to set attributes with a lifetime of a request (e.g.
237      * servlet request).
238      *
239      * @see #containsRequestAttribute
240      * @see #getRequestAttribute
241      * @see #setRequestAttribute
242      */
243     private final Map<String, Object> requestAttributes = new HashMap<String, Object>();
244 
245     /**
246      * A map to be able to set attributes with a lifetime of a session (e.g. as
247      * long as the user is logged in).
248      *
249      * @see #containsSessionAttribute
250      * @see #getSessionAttribute
251      * @see #setSessionAttribute
252      */
253     private Map<String, Object> sessionAttributes = new HashMap<String, Object>();
254 
255     /**
256      * Holds the timezone belonging to the user of this context.
257      */
258     private DateTimeZone timezone;
259 
260     /**
261      * Holds the locale belonging to the user of this context.
262      */
263     private Locale locale;
264 
265     /**
266      * Holds the chronology belonging to the user of this context.
267      */
268     private Chronology chronology;
269 
270     /**
271      * Holds the iso code of the language belonging to the user of this context.
272      */
273     private String language;
274 
275     /**
276      * If used in a webapp the context path of the webapp can be stored here, so
277      * that it is accessible for e.g. esjps.
278      */
279     private String path;
280 
281     /**
282      * Must the ThreadContext be inherit or not.
283      */
284     private final boolean inherit;
285 
286     /**
287      * Id of the request (means normally this instance of the context).
288      * Used for cacheing during a request.
289      */
290     private final String requestId;
291 
292     /**
293      * Private Constructor.
294      *
295      * @see #begin(String, Locale, Map, Map, Map)
296      *
297      * @param _transaction Transaction to be used in this context
298      * @param _locale Locale to be used in this context
299      * @param _sessionAttributes attributes belonging to this session
300      * @param _parameters parameters beloonging to this session
301      * @param _fileParameters paramters for file up/download
302      * @param _inherit              must the context be inherited to child threads
303      * @throws EFapsException on error
304      */
305     private Context(final Transaction _transaction,
306                     final Locale _locale,
307                     final Map<String, Object> _sessionAttributes,
308                     final Map<String, String[]> _parameters,
309                     final Map<String, FileParameter> _fileParameters,
310                     final boolean _inherit)
311         throws EFapsException
312     {
313         this.inherit = _inherit;
314         this.transaction = _transaction;
315         this.requestId = RandomStringUtils.randomAlphanumeric(8);
316         this.parameters = _parameters == null ? new HashMap<String, String[]>() : _parameters;
317         this.fileParameters = _fileParameters == null ? new HashMap<String, FileParameter>() : _fileParameters;
318         this.sessionAttributes = _sessionAttributes == null ? new HashMap<String, Object>() : _sessionAttributes;
319         try {
320             setConnection(Context.DATASOURCE.getConnection());
321         } catch (final SQLException e) {
322             Context.LOG.error("could not get a sql connection", e);
323         }
324     }
325 
326     /**
327      * @return ThreadLocal related to this context
328      */
329     private ThreadLocal<Context> getThreadLocal()
330     {
331         ThreadLocal<Context> ret;
332         if (this.inherit) {
333             ret = Context.INHERITTHREADCONTEXT;
334         } else {
335             ret = Context.THREADCONTEXT;
336         }
337         return ret;
338     }
339 
340     /**
341      * Destructor of class <code>Context</code>.
342      */
343     @Override
344     public void finalize()
345     {
346         if (Context.LOG.isDebugEnabled()) {
347             Context.LOG.debug("finalize context for " + this.person);
348             Context.LOG.debug("connection is " + getConnection());
349         }
350         if (this.connection != null) {
351             try {
352                 this.connection.close();
353             } catch (final SQLException e) {
354                 Context.LOG.error("could not close a sql connection", e);
355             }
356         }
357     }
358 
359     /**
360      * The method tests if all resources (JDBC connection and store resources)
361      * are closed, that means that the resources are freeed and returned for
362      * reuse.
363      *
364      * @return <i>true</i> if all resources are closed, otherwise <i>false</i>
365      *         is returned
366      * @see #connectionStore
367      * @see #storeStore
368      */
369     public boolean allConnectionClosed()
370     {
371         boolean closed = true;
372 
373         for (final ConnectionResource con : this.connectionStore) {
374             if (con.isOpened()) {
375                 closed = false;
376                 break;
377             }
378         }
379         if (closed) {
380             for (final Resource store : this.storeStore) {
381                 if (store.isOpened()) {
382                     closed = false;
383                     break;
384                 }
385             }
386         }
387         return closed;
388     }
389 
390     /**
391      * Close this contexts, meaning this context object is removed as thread
392      * context.<br/>
393      * If not all connection are closed, all connection are closed.
394      *
395      */
396     public void close()
397     {
398         if (Context.LOG.isDebugEnabled()) {
399             Context.LOG.debug("close context for " + this.person);
400             Context.LOG.debug("connection is " + getConnection());
401         }
402         QueryCache.cleanByKey(getRequestId());
403         if (this.connection != null) {
404             try {
405                 //this.connection.commit();
406                 this.connection.close();
407             } catch (final SQLException e) {
408                 Context.LOG.error("could not close a sql connection", e);
409             }
410         }
411 
412         setConnection(null);
413         if (getThreadLocal().get() != null && getThreadLocal().get() == this) {
414             getThreadLocal().set(null);
415         }
416         // check if all JDBC connection are close...
417         for (final ConnectionResource con : this.connectionStore) {
418             try {
419                 if (con.getConnection() != null && !con.getConnection().isClosed()) {
420                     con.getConnection().close();
421                     Context.LOG.error("connection was not closed!");
422                 }
423             } catch (final SQLException e) {
424                 Context.LOG.error("QLException is thrown while trying to get close status of "
425                                 + "connection or while trying to close", e);
426             }
427         }
428     }
429 
430     /**
431      * Method to abort the transaction.
432      *
433      * @throws EFapsException if setting of rollback was not successfully
434      */
435     public void abort()
436         throws EFapsException
437     {
438         try {
439             this.transaction.setRollbackOnly();
440         } catch (final SystemException e) {
441             throw new EFapsException(getClass(), "abort.SystemException", e);
442         }
443     }
444 
445     /**
446      * Returns a opened connection resource. If a previous close connection
447      * resource already exists, this already existing connection resource is
448      * returned.
449      *
450      * @return opened connection resource
451      * @throws EFapsException if connection resource cannot be created
452      */
453     public ConnectionResource getConnectionResource()
454         throws EFapsException
455     {
456         ConnectionResource con = null;
457         if (this.connectionStack.isEmpty()) {
458             try {
459                 con = new ConnectionResource(Context.DATASOURCE.getConnection());
460             } catch (final SQLException e) {
461                 throw new EFapsException(getClass(), "getConnectionResource.SQLException", e);
462             }
463             this.connectionStore.add(con);
464         } else {
465             con = this.connectionStack.pop();
466         }
467         if (!con.isOpened()) {
468             con.open();
469         }
470         return con;
471     }
472 
473     /**
474      * @param _con ConnectionResource
475      */
476     public void returnConnectionResource(final ConnectionResource _con)
477     {
478         this.connectionStack.push(_con);
479     }
480 
481     /**
482      * Method to get the sore resource.
483      *
484      * @param _instance Instance to get the StoreResource for
485      * @param _event    StorEvent the store is wanted for
486      * @throws EFapsException on error
487      * @return StoreResource
488      * @see #getStoreResource(Type,long)
489      */
490     public Resource getStoreResource(final Instance _instance,
491                                      final Resource.StoreEvent _event)
492         throws EFapsException
493     {
494         Resource storeRsrc = null;
495         final Store store = Store.get(_instance.getType().getStoreId());
496         storeRsrc = store.getResource(_instance);
497         storeRsrc.open(_event);
498         this.storeStore.add(storeRsrc);
499         return storeRsrc;
500     }
501 
502     /**
503      * If a person is assigned to this context, the id of this person is
504      * returned. Otherwise the default person id value is returned. The method
505      * guarantees to return value which is valid!<br/>
506      * The value could be used e.g. if a a value is inserted into the database
507      * and the person id is needed for the creator and / or modifier.
508      *
509      * @return person id of current person or default person id value
510      */
511     public long getPersonId()
512     {
513         long ret = 1;
514 
515         if (this.person != null) {
516             ret = this.person.getId();
517         }
518         return ret;
519     }
520 
521     /**
522      * Method to get a parameter from the context.
523      *
524      * @param _key Key for the parameter
525      * @return String value of the parameter
526      */
527     public String getParameter(final String _key)
528     {
529         String value = null;
530         if (this.parameters != null) {
531             final String[] values = this.parameters.get(_key);
532             if (values != null && values.length > 0) {
533                 value = values[0];
534             }
535         }
536         return value;
537     }
538 
539     /**
540      * Getter method for instance variable {@link #path}.
541      *
542      * @return value of instance variable {@link #path}
543      */
544     public String getPath()
545     {
546         return this.path;
547     }
548 
549     /**
550      * Setter method for instance variable {@link #path}.
551      *
552      * @param _path value for instance variable {@link #path}
553      */
554     public void setPath(final String _path)
555     {
556         this.path = _path;
557     }
558 
559     /**
560      * Getter method for the instance variable {@link #requestId}.
561      *
562      * @return value of instance variable {@link #requestId}
563      */
564     public String getRequestId()
565     {
566         return this.requestId;
567     }
568 
569     /**
570      * Returns true if request attributes maps one or more keys to the specified
571      * object. More formally, returns <i>true</i> if and only if the request
572      * attributes contains at least one mapping to a object o such that (o==null
573      * ? o==null : o.equals(o)).
574      *
575      * @param _key key whose presence in the request attributes is to be tested
576      * @return <i>true</i> if the request attributes contains a mapping for
577      *         given key, otherwise <i>false</i>
578      * @see #requestAttributes
579      * @see #getRequestAttribute
580      * @see #setRequestAttribute
581      */
582     public boolean containsRequestAttribute(final String _key)
583     {
584         return this.requestAttributes.containsKey(_key);
585     }
586 
587     /**
588      * Returns the object to which this request attributes maps the specified
589      * key. Returns <code>null</code> if the request attributes contains no
590      * mapping for this key. A return value of <code>null</code> does not
591      * necessarily indicate that the request attributes contains no mapping for
592      * the key; it's also possible that the request attributes explicitly maps
593      * the key to null. The {@link #containsRequestAttribute} operation may be
594      * used to distinguish these two cases.<br/>
595      * More formally, if the request attributes contains a mapping from a key k
596      * to a object o such that (key==null ? k==null : key.equals(k)), then this
597      * method returns o; otherwise it returns <code>null</code> (there can be at
598      * most one such mapping).
599      *
600      * @param _key key name of the mapped attribute to be returned
601      * @return object to which the request attribute contains a mapping for
602      *         specified key, or <code>null</code> if not specified in the
603      *         request attributes
604      * @see #requestAttributes
605      * @see #containsRequestAttribute
606      * @see #setRequestAttribute
607      */
608     public Object getRequestAttribute(final String _key)
609     {
610         return this.requestAttributes.get(_key);
611     }
612 
613     /**
614      * Associates the specified value with the specified key in the request
615      * attributes. If the request attributes previously contained a mapping for
616      * this key, the old value is replaced by the specified value.
617      *
618      * @param _key key name of the attribute to set
619      * @param _value _value of the attribute to set
620      * @return Object
621      * @see #requestAttributes
622      * @see #containsRequestAttribute
623      * @see #getRequestAttribute
624      */
625     public Object setRequestAttribute(final String _key,
626                                       final Object _value)
627     {
628         return this.requestAttributes.put(_key, _value);
629     }
630 
631     /**
632      * Returns true if session attributes maps one or more keys to the specified
633      * object. More formally, returns <i>true</i> if and only if the session
634      * attributes contains at least one mapping to a object o such that (o==null
635      * ? o==null : o.equals(o)).
636      *
637      * @param _key key whose presence in the session attributes is to be tested
638      * @return <i>true</i> if the session attributes contains a mapping for
639      *         given key, otherwise <i>false</i>
640      * @see #sessionAttributes
641      * @see #getSessionAttribute
642      * @see #setSessionAttribute
643      */
644     public boolean containsSessionAttribute(final String _key)
645     {
646         return this.sessionAttributes.containsKey(_key);
647     }
648 
649     /**
650      * Returns the object to which this session attributes maps the specified
651      * key. Returns <code>null</code> if the session attributes contains no
652      * mapping for this key. A return value of <code>null</code> does not
653      * necessarily indicate that the session attributes contains no mapping for
654      * the key; it's also possible that the session attributes explicitly maps
655      * the key to null. The {@link #containsSessionAttribute} operation may be
656      * used to distinguish these two cases.<br/>
657      * More formally, if the session attributes contains a mapping from a key k
658      * to a object o such that (key==null ? k==null : key.equals(k)), then this
659      * method returns o; otherwise it returns <code>null</code> (there can be at
660      * most one such mapping).
661      *
662      * @param _key key name of the mapped attribute to be returned
663      * @return object to which the session attribute contains a mapping for
664      *         specified key, or <code>null</code> if not specified in the
665      *         session attributes
666      * @see #sessionAttributes
667      * @see #containsSessionAttribute
668      * @see #setSessionAttribute
669      */
670     public Object getSessionAttribute(final String _key)
671     {
672         return this.sessionAttributes.get(_key);
673     }
674 
675     /**
676      * Associates the specified value with the specified key in the session
677      * attributes. If the session attributes previously contained a mapping for
678      * this key, the old value is replaced by the specified value.
679      *
680      * @param _key key name of the attribute to set
681      * @param _value value of the attribute to set
682      * @return Object
683      * @see #sessionAttributes
684      * @see #containsSessionAttribute
685      * @see #getSessionAttribute
686      */
687     public Object setSessionAttribute(final String _key,
688                                       final Object _value)
689     {
690         return this.sessionAttributes.put(_key, _value);
691     }
692 
693     /**
694      * Remove a attribute form the Session.
695      * @param _key key of the session attribute to be removed.
696      */
697     public void removeSessionAttribute(final String _key)
698     {
699         this.sessionAttributes.remove(_key);
700     }
701 
702     /**
703      * This method retrieves a UserAttribute of the Person this Context belongs
704      * to. The UserAttributes are stored in the {@link #sessionAttributes} Map,
705      * therefore are thought to be valid for one session.
706      *
707      * @param _key key to Search for
708      * @return String with the value
709      * @throws EFapsException on error
710      */
711     public String getUserAttribute(final String _key)
712         throws EFapsException
713     {
714         if (containsSessionAttribute(UserAttributesSet.CONTEXTMAPKEY)) {
715             return ((UserAttributesSet) getSessionAttribute(UserAttributesSet.CONTEXTMAPKEY)).getString(_key);
716         } else {
717             throw new EFapsException(Context.class, "getUserAttribute.NoSessionAttribute");
718         }
719     }
720 
721     /**
722      * This method determines if UserAttribute of the Person this Context
723      * belongs to exists.The UserAttributes are stored in the
724      * {@link #sessionAttributes} Map, therefore are thought to be valid for one
725      * session.
726      *
727      * @param _key key to Search for
728      * @return true if found, else false
729      *
730      * @throws EFapsException on error
731      */
732     public boolean containsUserAttribute(final String _key)
733         throws EFapsException
734     {
735         boolean ret = false;
736         if (containsSessionAttribute(UserAttributesSet.CONTEXTMAPKEY)) {
737             ret = ((UserAttributesSet) getSessionAttribute(UserAttributesSet.CONTEXTMAPKEY)).containsKey(_key);
738         }
739         return ret;
740     }
741 
742     /**
743      * Set a new UserAttribute for the UserAttribute of the Person this
744      * Context.The UserAttributes are stored in the {@link #sessionAttributes}
745      * Map, therefore are thought to be valid for one session.
746      *
747      * @param _key Key of the UserAttribute
748      * @param _value Value of the UserAttribute
749      * @throws EFapsException on error
750      */
751     public void setUserAttribute(final String _key,
752                                  final String _value)
753         throws EFapsException
754     {
755         if (containsSessionAttribute(UserAttributesSet.CONTEXTMAPKEY)) {
756             ((UserAttributesSet) getSessionAttribute(UserAttributesSet.CONTEXTMAPKEY)).set(_key, _value);
757         } else {
758             final UserAttributesSet userAttribute = new UserAttributesSet(getPersonId());
759             userAttribute.set(_key, _value);
760             setSessionAttribute(UserAttributesSet.CONTEXTMAPKEY, userAttribute);
761         }
762     }
763 
764     /**
765      * Set a new UserAttribute for the UserAttribute of the Person this
766      * Context.The UserAttributes are stored in the {@link #sessionAttributes}
767      * Map, therefore are thought to be valid for one session.
768      *
769      * @param _key Key of the UserAttribute
770      * @param _value Value of the UserAttribute
771      * @param _definition Definition
772      * @throws EFapsException on error
773      */
774     public void setUserAttribute(final String _key,
775                                  final String _value,
776                                  final UserAttributesDefinition _definition)
777         throws EFapsException
778     {
779         if (containsSessionAttribute(UserAttributesSet.CONTEXTMAPKEY)) {
780             ((UserAttributesSet) getSessionAttribute(UserAttributesSet.CONTEXTMAPKEY)).set(_key, _value, _definition);
781         } else {
782             throw new EFapsException(Context.class, "getUserAttributes.NoSessionAttribute");
783         }
784     }
785 
786     /**
787      * Method to get the UserAttributesSet of the user of this context.
788      *
789      * @return UserAttributesSet
790      * @throws EFapsException on error
791      */
792     public UserAttributesSet getUserAttributes()
793         throws EFapsException
794     {
795         if (containsSessionAttribute(UserAttributesSet.CONTEXTMAPKEY)) {
796             return (UserAttributesSet) getSessionAttribute(UserAttributesSet.CONTEXTMAPKEY);
797         } else {
798             throw new EFapsException(Context.class, "getUserAttributes.NoSessionAttribute");
799         }
800     }
801 
802     /**
803      * This is the getter method for instance variable {@link #connection}.
804      *
805      * @return value of instance variable {@link #connection}
806      * @see #connection
807      * @see #setConnection
808      */
809     public Connection getConnection()
810     {
811         return this.connection;
812     }
813 
814     /**
815      * This is the setter method for instance variable {@link #connection}.
816      *
817      * @param _connection new value for instance variable {@link #connection}
818      * @see #connection
819      * @see #getConnection
820      */
821     private void setConnection(final Connection _connection)
822     {
823         this.connection = _connection;
824     }
825 
826     /**
827      * @param _transaction Transaction to set
828      */
829     private void setTransaction(final Transaction _transaction)
830     {
831         this.transaction = _transaction;
832     }
833 
834     /**
835      * This is the getter method for instance variable {@link #transaction}.
836      *
837      * @return value of instance variable {@link #transaction}
838      * @see #transaction
839      */
840     public Transaction getTransaction()
841     {
842         return this.transaction;
843     }
844 
845     /**
846      * This is the getter method for instance variable {@link #person}.
847      *
848      * @return value of instance variable {@link #person}
849      * @see #person
850      */
851     public Person getPerson()
852     {
853         return this.person;
854     }
855 
856     /**
857      * Get the Company currently valid for this context.
858      *
859      * @return value of instance variable {@link #company}
860      * @throws CacheReloadException on error
861      */
862     public Company getCompany()
863         throws CacheReloadException
864     {
865         return this.companyId == null ?  null : Company.get(this.companyId);
866     }
867 
868     /**
869      * Set the Company currently valid for this context.
870      * @param _company Company to set
871      * @throws CacheReloadException on error
872      */
873     public void setCompany(final Company _company)
874         throws CacheReloadException
875     {
876         if (_company == null) {
877             this.companyId = null;
878         } else {
879             this.companyId = _company.getId();
880         }
881     }
882 
883     /**
884      * This is the getter method for instance variable {@link #locale}.
885      *
886      * @return value of instance variable {@link #locale}
887      * @see #locale
888      */
889     public Locale getLocale()
890     {
891         return this.locale;
892     }
893 
894     /**
895      * This is the getter method for instance variable {@link #timezone}.
896      *
897      * @return value of instance variable {@link #timezone}
898      * @see #timezone
899      */
900     public DateTimeZone getTimezone()
901     {
902         return this.timezone;
903     }
904 
905     /**
906      * This is the getter method for instance variable {@link #chronology}.
907      *
908      * @return value of instance variable {@link #chronology}
909      * @see #locale
910      */
911     public Chronology getChronology()
912     {
913         return this.chronology;
914     }
915 
916     /**
917      * Getter method for instance variable {@link #language}.
918      *
919      * @return value of instance variable {@link #language}
920      */
921     public String getLanguage()
922     {
923         return this.language;
924     }
925 
926     /**
927      * This is the getter method for instance variable {@link #parameters}.
928      *
929      * @return value of instance variable {@link #parameters}
930      * @see #parameters
931      */
932     public Map<String, String[]> getParameters()
933     {
934         return this.parameters;
935     }
936 
937     /**
938      * This is the getter method for instance variable {@link #fileParameters}.
939      *
940      * @return value of instance variable {@link #fileParameters}
941      * @see #fileParameters
942      */
943     public Map<String, FileParameter> getFileParameters()
944     {
945         return this.fileParameters;
946     }
947 
948     /**
949      * Is a Thread active.
950      *
951      * @return true if either the ThreadContext or the
952      *              Inherited ThreadContext is no null
953      */
954     public static boolean isThreadActive()
955     {
956         return Context.INHERITTHREADCONTEXT.get() != null || Context.THREADCONTEXT.get() != null;
957     }
958 
959     /**
960      * The method checks if for the current thread a context object is defined.
961      * This found context object is returned.
962      *
963      * @return defined context object of current thread
964      * @throws EFapsException if no context object for current thread is defined
965      * @see #INHERITTHREADCONTEXT
966      */
967     public static Context getThreadContext()
968         throws EFapsException
969     {
970         Context context = Context.THREADCONTEXT.get();
971         if (context == null) {
972             context = Context.INHERITTHREADCONTEXT.get();
973         }
974         if (context == null) {
975             throw new EFapsException(Context.class, "getThreadContext.NoContext4ThreadDefined");
976         }
977         return context;
978     }
979 
980     /**
981      * Method to get a new Context.
982      *
983      * @see #begin(String, Locale, Map, Map, Map)
984      * @throws EFapsException on error
985      * @return new Context
986      */
987     public static Context begin()
988         throws EFapsException
989     {
990         return Context.begin(null, null, null, null, null, true);
991     }
992 
993     /**
994      * Method to get a new Context.
995      *
996      * @see #begin(String, Locale, Map, Map, Map)
997      * @param _userName Naem of the user the Context must be created for
998      * @throws EFapsException on error
999      * @return new Context
1000      *
1001      */
1002     public static Context begin(final String _userName)
1003         throws EFapsException
1004     {
1005         return Context.begin(_userName, null, null, null, null, true);
1006     }
1007 
1008     /**
1009      * Method to get a new Context.
1010      *
1011      * @see #begin(String, Locale, Map, Map, Map)
1012      * @param _userName Naem of the user the Context must be created for
1013      * @param _inherit              must the context be inherited to child threads
1014      * @throws EFapsException on error
1015      * @return new Context
1016      *
1017      */
1018     public static Context begin(final String _userName,
1019                                 final boolean _inherit)
1020         throws EFapsException
1021     {
1022         return Context.begin(_userName, null, null, null, null, _inherit);
1023     }
1024 
1025     /**
1026      * For current thread a new context object must be created.
1027      *
1028      * @param _userName             name of current user to set
1029      * @param _locale               locale instance (which language settings has the user)
1030      * @param _sessionAttributes    attributes for this session
1031      * @param _parameters           map with parameters for this thread context
1032      * @param _fileParameters       map with file parameters
1033      * @param _inherit              must the context be inherited to child threads
1034      * @return new context of thread
1035      * @throws EFapsException if a new transaction could not be started or if
1036      *             current thread context is already set
1037      * @see #INHERITTHREADCONTEXT
1038      */
1039     public static Context begin(final String _userName,
1040                                 final Locale _locale,
1041                                 final Map<String, Object> _sessionAttributes,
1042                                 final Map<String, String[]> _parameters,
1043                                 final Map<String, FileParameter> _fileParameters,
1044                                 final boolean _inherit)
1045         throws EFapsException
1046     {
1047         if (_inherit && Context.INHERITTHREADCONTEXT.get() != null
1048                         || !_inherit  && Context.THREADCONTEXT.get() != null) {
1049             throw new EFapsException(Context.class, "begin.Context4ThreadAlreadSet");
1050         }
1051 
1052         try {
1053             // the timeout set is reseted on creation of a new Current object in
1054             // the transaction manager,
1055             // so if the default must be overwritten it must be set explicitly
1056             // again
1057             if (Context.TRANSMANAGTIMEOUT > 0) {
1058                 Context.TRANSMANAG.setTransactionTimeout(Context.TRANSMANAGTIMEOUT);
1059             }
1060             Context.TRANSMANAG.begin();
1061         } catch (final SystemException e) {
1062             throw new EFapsException(Context.class, "begin.beginSystemException", e);
1063         } catch (final NotSupportedException e) {
1064             throw new EFapsException(Context.class, "begin.beginNotSupportedException", e);
1065         }
1066         Transaction transaction;
1067         try {
1068             transaction = Context.TRANSMANAG.getTransaction();
1069         } catch (final SystemException e) {
1070             throw new EFapsException(Context.class, "begin.getTransactionSystemException", e);
1071         }
1072         final Context context = new Context(transaction, _locale == null ? Locale.ENGLISH : _locale,
1073                         _sessionAttributes, _parameters, _fileParameters, _inherit);
1074         if (_inherit) {
1075             Context.INHERITTHREADCONTEXT.set(context);
1076         } else {
1077             Context.THREADCONTEXT.set(context);
1078         }
1079 
1080         if (_userName != null) {
1081             context.person = Person.get(_userName);
1082             context.locale = context.person.getLocale();
1083             context.timezone = context.person.getTimeZone();
1084             context.chronology = context.person.getChronology();
1085             context.language = context.person.getLanguage();
1086             if (_sessionAttributes != null) {
1087                 if (context.containsUserAttribute(Context.CURRENTCOMPANY)) {
1088                     final Company comp = Company.get(Long.parseLong(context.getUserAttribute(Context.CURRENTCOMPANY)));
1089                     if (comp != null && !context.person.getCompanies().isEmpty() && context.person.isAssigned(comp)) {
1090                         context.companyId = comp.getId();
1091                     } else {
1092                         context.setUserAttribute(Context.CURRENTCOMPANY, "0");
1093                     }
1094                 }
1095                 // if no current company is set in the UserAttributes, the first one found is set
1096                 if (context.companyId == null && context.person.getCompanies().size() > 0) {
1097                     final Long compID = context.person.getCompanies().iterator().next();
1098                     context.setUserAttribute(Context.CURRENTCOMPANY, compID.toString());
1099                     context.companyId = compID;
1100                 }
1101             }
1102         }
1103         return context;
1104     }
1105 
1106 
1107     /**
1108      * Save the Context by committing and beginning a new Transaction.
1109      * @throws EFapsException on error
1110      */
1111     public static void save()
1112         throws EFapsException
1113     {
1114         try {
1115             Context.TRANSMANAG.commit();
1116             Context.TRANSMANAG.begin();
1117             final Context context = Context.getThreadContext();
1118             context.setTransaction(Context.TRANSMANAG.getTransaction());
1119             context.connectionStack.clear();
1120             context.connectionStore.clear();
1121         } catch (final SecurityException e) {
1122             throw new EFapsException(Context.class, "save.SecurityException", e);
1123         } catch (final IllegalStateException e) {
1124             throw new EFapsException(Context.class, "save.IllegalStateException", e);
1125         } catch (final RollbackException e) {
1126             throw new EFapsException(Context.class, "save.RollbackException", e);
1127         } catch (final HeuristicMixedException e) {
1128             throw new EFapsException(Context.class, "save.HeuristicMixedException", e);
1129         } catch (final HeuristicRollbackException e) {
1130             throw new EFapsException(Context.class, "save.HeuristicRollbackException", e);
1131         } catch (final SystemException e) {
1132             throw new EFapsException(Context.class, "save.SystemException", e);
1133         } catch (final NotSupportedException e) {
1134             throw new EFapsException(Context.class, "save.NotSupportedException", e);
1135         }
1136     }
1137 
1138     /**
1139      * Commit the context.
1140      * @throws EFapsException if commit of the transaction manager failed
1141      */
1142     public static void commit()
1143         throws EFapsException
1144     {
1145         Context.commit(true);
1146     }
1147 
1148     /**
1149      * @param _close close the threat context or not
1150      * @throws EFapsException if commit of the transaction manager failed
1151      */
1152     public static void commit(final boolean _close)
1153         throws EFapsException
1154     {
1155         try {
1156             Context.TRANSMANAG.commit();
1157         } catch (final IllegalStateException e) {
1158             throw new EFapsException(Context.class, "commit.IllegalStateException", e);
1159         } catch (final SecurityException e) {
1160             throw new EFapsException(Context.class, "commit.SecurityException", e);
1161         } catch (final HeuristicMixedException e) {
1162             throw new EFapsException(Context.class, "commit.HeuristicMixedException", e);
1163         } catch (final HeuristicRollbackException e) {
1164             throw new EFapsException(Context.class, "commit.HeuristicRollbackException", e);
1165         } catch (final RollbackException e) {
1166             throw new EFapsException(Context.class, "commit.RollbackException", e);
1167         } catch (final SystemException e) {
1168             throw new EFapsException(Context.class, "commit.SystemException", e);
1169         } finally {
1170             if (_close) {
1171                 Context.getThreadContext().close();
1172             }
1173         }
1174     }
1175 
1176     /**
1177      * @throws EFapsException if roll back of the transaction manager failed
1178      */
1179     public static void rollback()
1180         throws EFapsException
1181     {
1182         try {
1183             Context.TRANSMANAG.rollback();
1184         } catch (final IllegalStateException e) {
1185             throw new EFapsException(Context.class, "rollback.IllegalStateException", e);
1186         } catch (final SecurityException e) {
1187             throw new EFapsException(Context.class, "rollback.SecurityException", e);
1188         } catch (final SystemException e) {
1189             throw new EFapsException(Context.class, "rollback.SystemException", e);
1190         } finally {
1191             Context.getThreadContext().close();
1192         }
1193     }
1194 
1195     /**
1196      * Is the status of transaction manager active?
1197      *
1198      * @return <i>true</i> if transaction manager is active, otherwise
1199      *         <i>false</i>
1200      * @throws EFapsException if the status of the transaction manager could not
1201      *             be evaluated
1202      * @see #TRANSMANAG
1203      */
1204     public static boolean isTMActive()
1205         throws EFapsException
1206     {
1207         try {
1208             return Context.TRANSMANAG.getStatus() == Status.STATUS_ACTIVE;
1209         } catch (final SystemException e) {
1210             throw new EFapsException(Context.class, "isTMActive.SystemException", e);
1211         }
1212     }
1213 
1214     /**
1215      * Is a transaction associated with a target object for transaction manager?
1216      *
1217      * @return <i>true</i> if a transaction associated, otherwise <i>false</i>
1218      * @throws EFapsException if the status of the transaction manager could not
1219      *             be evaluated
1220      * @see #TRANSMANAG
1221      */
1222     public static boolean isTMNoTransaction()
1223         throws EFapsException
1224     {
1225         try {
1226             return Context.TRANSMANAG.getStatus() == Status.STATUS_NO_TRANSACTION;
1227         } catch (final SystemException e) {
1228             throw new EFapsException(Context.class, "isTMNoTransaction.SystemException", e);
1229         }
1230     }
1231 
1232     /**
1233      * Is the status of transaction manager marked roll back?
1234      *
1235      * @return <i>true</i> if transaction manager is marked roll back, otherwise
1236      *         <i>false</i>
1237      * @throws EFapsException if the status of the transaction manager could not
1238      *             be evaluated
1239      * @see #TRANSMANAG
1240      */
1241     public static boolean isTMMarkedRollback()
1242         throws EFapsException
1243     {
1244         try {
1245             return Context.TRANSMANAG.getStatus() == Status.STATUS_MARKED_ROLLBACK;
1246         } catch (final SystemException e) {
1247             throw new EFapsException(Context.class, "isTMMarkedRollback.SystemException", e);
1248         }
1249     }
1250 
1251     /**
1252      * Returns the database type of the default connection (database where the
1253      * data model definition is stored).
1254      *
1255      * @see #DBTYPE
1256      * @return AbstractDatabase
1257      */
1258     public static AbstractDatabase<?> getDbType()
1259     {
1260         return Context.DBTYPE;
1261     }
1262 
1263     /**
1264      * Resets the context to current defined values in the Javax naming
1265      * environment.
1266      *
1267      * @throws StartupException if context could not be reseted to new values
1268      * @see #DBTYPE
1269      * @see #DATASOURCE
1270      * @see #TRANSMANAG
1271      * @see #TRANSMANAGTIMEOUT
1272      */
1273     public static void reset()
1274         throws StartupException
1275     {
1276         try {
1277             final InitialContext initCtx = new InitialContext();
1278             final javax.naming.Context envCtx = (javax.naming.Context) initCtx.lookup("java:comp/env");
1279             Context.DBTYPE = (AbstractDatabase<?>) envCtx.lookup(INamingBinds.RESOURCE_DBTYPE);
1280             Context.DATASOURCE = (DataSource) envCtx.lookup(INamingBinds.RESOURCE_DATASOURCE);
1281             Context.TRANSMANAG = (TransactionManager) envCtx.lookup(INamingBinds.RESOURCE_TRANSMANAG);
1282             try {
1283                 Context.TRANSMANAGTIMEOUT = 0;
1284                 final Map<?, ?> props = (Map<?, ?>) envCtx.lookup(INamingBinds.RESOURCE_CONFIGPROPERTIES);
1285                 if (props != null) {
1286                     final String transactionTimeoutString = (String) props.get(IeFapsProperties.TRANSACTIONTIMEOUT);
1287                     if (transactionTimeoutString != null) {
1288                         Context.TRANSMANAGTIMEOUT = Integer.parseInt(transactionTimeoutString);
1289                     }
1290                 }
1291             } catch (final NamingException e) {
1292                 // this is actual no error, so nothing is presented
1293                 Context.TRANSMANAGTIMEOUT = 0;
1294             }
1295         } catch (final NamingException e) {
1296             throw new StartupException("eFaps context could not be initialized", e);
1297         }
1298     }
1299 
1300     /**
1301      * Interfaces defining file parameters used to access file parameters (e.g.
1302      * uploads from the user within the web application).
1303      */
1304     public interface FileParameter
1305     {
1306 
1307         /**
1308          * Closes the file for this this file parameter is defined (e.g. deletes
1309          * the file in the temporary directory, if needed).
1310          *
1311          * @throws IOException if the close failed
1312          */
1313         void close()
1314             throws IOException;
1315 
1316         /**
1317          * Returns the input stream of the file for which this file parameter is
1318          * defined.
1319          *
1320          * @return input stream of the file
1321          * @throws IOException if the input stream could not be returned
1322          */
1323         InputStream getInputStream()
1324             throws IOException;
1325 
1326         /**
1327          * Returns the size of the file for which this file parameter is
1328          * defined.
1329          *
1330          * @return size of file
1331          */
1332         long getSize();
1333 
1334         /**
1335          * Returns the content type of the file for which this file parameter is
1336          * defined.
1337          *
1338          * @return content type of the file
1339          */
1340         String getContentType();
1341 
1342         /**
1343          * Returns the name of the file for which this file parameter is
1344          * defined.
1345          *
1346          * @return name of file
1347          */
1348         String getName();
1349 
1350         /**
1351          * Returns the name of the parameter for which this file parameter is
1352          * defined.
1353          *
1354          * @return parameter name
1355          */
1356         String getParameterName();
1357     }
1358 }