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 }