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.message;
22  
23  import java.sql.PreparedStatement;
24  import java.sql.ResultSet;
25  import java.sql.SQLException;
26  import java.util.HashMap;
27  import java.util.Map;
28  import java.util.UUID;
29  
30  import org.efaps.admin.datamodel.Status;
31  import org.efaps.db.Context;
32  import org.efaps.db.transaction.ConnectionResource;
33  import org.efaps.util.EFapsException;
34  import org.quartz.Job;
35  import org.quartz.JobExecutionContext;
36  import org.quartz.JobExecutionException;
37  import org.slf4j.Logger;
38  import org.slf4j.LoggerFactory;
39  
40  /**
41   * Holds the status of the messages including their count.
42   *
43   * @author The eFaps Team
44   * @version $Id$
45   */
46  public class MessageStatusHolder
47      implements Job
48  {
49      /**
50       * Logging instance used in this class.
51       */
52      private static final Logger LOG = LoggerFactory.getLogger(MessageStatusHolder.class);
53  
54      /**
55       * basis select.
56       */
57      private static final String SELECT = "SELECT DISTINCT userid, count(*) "
58          + "FROM t_msg2user WHERE status = ? GROUP BY userid";
59  
60      /**
61       * Cache used for the MessageStatus.
62       */
63      private static final MsgCache CACHE = new MsgCache();
64  
65      /**
66       * Execute the job.
67       * @param _context JobExecutionContext
68       * @throws JobExecutionException on error
69       */
70      @Override
71      public void execute(final JobExecutionContext _context)
72          throws JobExecutionException
73      {
74          MessageStatusHolder.CACHE.update();
75      }
76  
77      /**
78       * has the given user read messages.
79       * @param _userId   user id the status is wanted for
80       * @return true if unread exist
81       */
82      public static boolean hasReadMsg(final Long _userId)
83      {
84          return MessageStatusHolder.CACHE.userID2Read.containsKey(_userId);
85      }
86  
87      /**
88       * Count of read messages.
89       * @param _userId  user id the count is wanted for
90       * @return count of read messages
91       */
92      public static int getReadCount(final Long _userId)
93      {
94          int ret = 0;
95          if (MessageStatusHolder.CACHE.userID2Read.containsKey(_userId)) {
96              ret = MessageStatusHolder.CACHE.userID2Read.get(_userId);
97          }
98          return ret;
99      }
100 
101     /**
102      * has the given user unread messages.
103      * @param _userId   user id the status is wanted for
104      * @return true if unread exist
105      */
106     public static boolean hasUnreadMsg(final Long _userId)
107     {
108         return MessageStatusHolder.CACHE.userID2UnRead.containsKey(_userId);
109     }
110 
111     /**
112      * Count of unread messages.
113      * @param _userId  user id the count is wanted for
114      * @return count of unread messages
115      */
116     public static int getUnReadCount(final Long _userId)
117     {
118         int ret = 0;
119         if (MessageStatusHolder.CACHE.userID2UnRead.containsKey(_userId)) {
120             ret = MessageStatusHolder.CACHE.userID2UnRead.get(_userId);
121         }
122         return ret;
123     }
124 
125     /**
126      * Thread save cache.
127      */
128     private static final class MsgCache
129     {
130         /**
131          * The map holds all cached data instances by Id. Because of the
132          * double-checked locking idiom, the instance variable is defined
133          * <i>volatile</i>.
134          */
135         private volatile Map<Long, Integer> userID2UnRead = null;
136 
137         /**
138          * The map holds all cached data instances by Id. Because of the
139          * double-checked locking idiom, the instance variable is defined
140          * <i>volatile</i>.
141          */
142         private volatile Map<Long, Integer> userID2Read = null;
143 
144         /**
145          * Constructor setting empty map.
146          */
147         private MsgCache()
148         {
149             this.userID2UnRead = new HashMap<Long,  Integer>();
150             this.userID2Read = new HashMap<Long,  Integer>();
151         }
152 
153         /**
154          * Update the MessageStatus.
155          */
156         public void update()
157         {
158             ConnectionResource con = null;
159             boolean abort = true;
160             try {
161                 con = Context.getThreadContext().getConnectionResource();
162 
163                 final Map<Long, Integer> unread = new HashMap<Long, Integer>();
164                 final Map<Long, Integer> read = new HashMap<Long, Integer>();
165 
166                 final PreparedStatement stmt = con.getConnection().prepareStatement(MessageStatusHolder.SELECT);
167                 stmt.setLong(1, Status.find(UUID.fromString("87b82fee-69d3-4e45-aced-0d57c6a0cd1d"), "Unread").getId());
168 
169                 final ResultSet rs = stmt.executeQuery();
170 
171                 while (rs.next()) {
172                     final long id = rs.getLong(1);
173                     final Integer count = rs.getInt(2);
174                     unread.put(id, count);
175                 }
176                 rs.close();
177 
178                 stmt.setLong(1, Status.find(UUID.fromString("87b82fee-69d3-4e45-aced-0d57c6a0cd1d"), "Read").getId());
179                 final ResultSet rs2 = stmt.executeQuery();
180 
181                 while (rs2.next()) {
182                     final long id = rs2.getLong(1);
183                     final Integer count = rs2.getInt(2);
184                     read.put(id, count);
185                 }
186                 rs2.close();
187                 stmt.close();
188                 con.commit();
189                 this.userID2UnRead = unread;
190                 this.userID2Read = read;
191                 abort = false;
192             } catch (final EFapsException e) {
193                 MessageStatusHolder.LOG.error("EFapsException");
194             } catch (final IllegalStateException e) {
195                 MessageStatusHolder.LOG.error("IllegalStateException");
196             } catch (final SQLException e) {
197                 MessageStatusHolder.LOG.error("SQLException");
198             } finally {
199                 if (abort && con != null) {
200                     try {
201                         con.abort();
202                     } catch (final EFapsException e) {
203                         MessageStatusHolder.LOG.error("EFapsException");
204                     }
205                 }
206             }
207         }
208     }
209 }
210