1   /**
2    * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
3    *
4    * This library is free software; you can redistribute it and/or modify it under
5    * the terms of the GNU Lesser General Public License as published by the Free
6    * Software Foundation; either version 2.1 of the License, or (at your option)
7    * any later version.
8    *
9    * This library is distributed in the hope that it will be useful, but WITHOUT
10   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11   * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12   * details.
13   */
14  
15  package com.liferay.portal.util;
16  
17  import com.liferay.portal.kernel.dao.db.DB;
18  import com.liferay.portal.kernel.dao.db.DBFactoryUtil;
19  import com.liferay.portal.kernel.dao.jdbc.DataAccess;
20  import com.liferay.portal.kernel.util.FileUtil;
21  import com.liferay.portal.kernel.util.MultiValueMap;
22  import com.liferay.portal.kernel.util.SerializableUtil;
23  
24  import java.io.File;
25  import java.io.Serializable;
26  
27  import java.sql.Connection;
28  import java.sql.DriverManager;
29  import java.sql.PreparedStatement;
30  import java.sql.ResultSet;
31  
32  import java.util.Collection;
33  import java.util.HashSet;
34  import java.util.Set;
35  
36  /**
37   * <a href="FileMultiValueMap.java.html"><b><i>View Source</i></b></a>
38   *
39   * @author Alexander Chow
40   */
41  public class FileMultiValueMap<K extends Serializable, V extends Serializable>
42      extends MultiValueMap<K, V> {
43  
44      public FileMultiValueMap() {
45          _fileName = FileUtil.createTempFileName();
46  
47          try {
48              Class.forName("org.hsqldb.jdbcDriver");
49          }
50          catch (Exception e) {
51              throw new RuntimeException(e);
52          }
53  
54          _createDatabase();
55      }
56  
57      public void clear() {
58          try {
59              _deleteDatabase();
60              _createDatabase();
61          }
62          catch (Throwable t) {
63              throw new RuntimeException(t);
64          }
65      }
66  
67      public boolean containsKey(Object key) {
68          int count = _getCount((K)key, null);
69  
70          if (count > 0) {
71              return true;
72          }
73          else {
74              return false;
75          }
76      }
77  
78      public boolean containsValue(Object value) {
79          int count = _getCount(null, (V)value);
80  
81          if (count > 0) {
82              return true;
83          }
84          else {
85              return false;
86          }
87      }
88  
89      public Set<V> getAll(Object key) {
90          Connection con = null;
91          PreparedStatement ps = null;
92          ResultSet rs = null;
93  
94          Set<V> values = null;
95  
96          try {
97              con = _getConnection();
98  
99              ps = con.prepareStatement("SELECT value_ FROM Map WHERE key_ = ?");
100 
101             ps.setBytes(1, SerializableUtil.serialize(key));
102 
103             rs = ps.executeQuery();
104 
105             while (rs.next()) {
106                 if (values == null) {
107                     values = new HashSet<V>();
108                 }
109 
110                 V value = null;
111 
112                 value = (V)SerializableUtil.deserialize(rs.getBytes(_VALUE));
113 
114                 values.add(value);
115             }
116         }
117         catch (Exception e) {
118             throw new RuntimeException(e);
119         }
120         finally {
121             DataAccess.cleanUp(con, ps, rs);
122         }
123 
124         return values;
125     }
126 
127     public boolean isEmpty() {
128         int count = _getCount(null, null);
129 
130         if (count == 0) {
131             return true;
132         }
133         else {
134             return false;
135         }
136     }
137 
138     public Set<K> keySet() {
139         Connection con = null;
140         PreparedStatement ps = null;
141         ResultSet rs = null;
142 
143         Set<K> keys = null;
144 
145         try {
146             con = _getConnection();
147 
148             ps = con.prepareStatement("SELECT DISTINCT (key_) FROM Map ");
149 
150             rs = ps.executeQuery();
151 
152             while (rs.next()) {
153                 if (keys == null) {
154                     keys = new HashSet<K>();
155                 }
156 
157                 K key = null;
158 
159                 key = (K)SerializableUtil.deserialize(rs.getBytes(_KEY));
160 
161                 keys.add(key);
162             }
163         }
164         catch (Exception e) {
165             throw new RuntimeException(e);
166         }
167         finally {
168             DataAccess.cleanUp(con, ps, rs);
169         }
170 
171         return keys;
172     }
173 
174     public V put(K key, V value) {
175         if ((key == null) || (value == null)) {
176             return null;
177         }
178 
179         if (_getCount(key, value) == 0) {
180             Connection con = null;
181             PreparedStatement ps = null;
182 
183             try {
184                 con = _getConnection();
185 
186                 ps = con.prepareStatement(
187                     "INSERT INTO Map (key_, value_) values (?, ?)");
188 
189                 ps.setBytes(1, SerializableUtil.serialize(key));
190                 ps.setBytes(2, SerializableUtil.serialize(value));
191 
192                 ps.execute();
193             }
194             catch (Exception e) {
195                 throw new RuntimeException(e);
196             }
197             finally {
198                 DataAccess.cleanUp(con, ps);
199             }
200         }
201 
202         return value;
203     }
204 
205     public Set<V> putAll(K key, Collection<? extends V> values) {
206         Set<V> curValues = getAll(key);
207 
208         if ((values == null) || values.isEmpty()) {
209             return curValues;
210         }
211 
212         if (curValues == null) {
213             values = new HashSet<V>();
214         }
215 
216         for (V value: values) {
217             if (!curValues.contains(value)) {
218                 curValues.add(value);
219 
220                 put(key, value);
221             }
222         }
223 
224         return curValues;
225     }
226 
227     public V remove(Object key) {
228         Connection con = null;
229         PreparedStatement ps = null;
230         ResultSet rs = null;
231 
232         V firstValue = null;
233 
234         try {
235             con = _getConnection();
236 
237             ps = con.prepareStatement("SELECT value_ FROM Map WHERE key_ = ?");
238 
239             ps.setBytes(1, SerializableUtil.serialize(key));
240 
241             rs = ps.executeQuery();
242 
243             if (rs.next()) {
244                 firstValue = (V)SerializableUtil.deserialize(
245                     rs.getBytes(_VALUE));
246             }
247         }
248         catch (Exception e) {
249             throw new RuntimeException(e);
250         }
251         finally {
252             DataAccess.cleanUp(con, ps, rs);
253         }
254 
255         try {
256             con = _getConnection();
257 
258             ps = con.prepareStatement("DELETE FROM Map WHERE key_ = ?");
259 
260             ps.setBytes(1, SerializableUtil.serialize(key));
261 
262             ps.execute();
263         }
264         catch (Exception e) {
265             throw new RuntimeException(e);
266         }
267         finally {
268             DataAccess.cleanUp(con, ps);
269         }
270 
271         return firstValue;
272     }
273 
274     protected void finalize() throws Throwable {
275         try {
276             _deleteDatabase();
277         }
278         finally {
279             super.finalize();
280         }
281     }
282 
283     private void _createDatabase() {
284         Connection con = null;
285 
286         try {
287             con = _getConnection();
288 
289             DB db = DBFactoryUtil.getDB(DB.TYPE_HYPERSONIC);
290 
291             db.runSQL(con, _CREATE_SQL);
292         }
293         catch (Exception e) {
294             throw new RuntimeException(e);
295         }
296         finally {
297             DataAccess.cleanUp(con);
298         }
299     }
300 
301     private void _deleteDatabase() throws Throwable {
302         File[] files = new File[] {
303             new File(_fileName + ".properties"),
304             new File(_fileName + ".script"),
305             new File(_fileName + ".log"),
306             new File(_fileName + ".data"),
307             new File(_fileName + ".backup")
308         };
309 
310         for (File file : files) {
311             if (file.exists()) {
312                 file.delete();
313             }
314         }
315     }
316 
317     private Connection _getConnection() throws Exception {
318         return DriverManager.getConnection(
319             "jdbc:hsqldb:file:" + _fileName, "sa", "");
320     }
321 
322     private int _getCount(K key, V value) {
323         Connection con = null;
324         PreparedStatement ps = null;
325         ResultSet rs = null;
326 
327         try {
328             con = _getConnection();
329 
330             String sql = "SELECT count(*) FROM Map ";
331 
332             if ((key != null) && (value != null)) {
333                 sql += "WHERE key_ = ? AND value_ = ?";
334 
335                 ps = con.prepareStatement(sql);
336 
337                 ps.setBytes(1, SerializableUtil.serialize(key));
338                 ps.setBytes(2, SerializableUtil.serialize(value));
339             }
340             else if (key != null) {
341                 sql += "WHERE key_ = ?";
342 
343                 ps = con.prepareStatement(sql);
344 
345                 ps.setBytes(1, SerializableUtil.serialize(key));
346             }
347             else if (value != null) {
348                 sql += "WHERE value_ = ?";
349 
350                 ps = con.prepareStatement(sql);
351 
352                 ps.setBytes(1, SerializableUtil.serialize(value));
353             }
354             else {
355                 ps = con.prepareStatement(sql);
356             }
357 
358             rs = ps.executeQuery();
359 
360             rs.next();
361 
362             return rs.getInt(1);
363         }
364         catch (Exception e) {
365             throw new RuntimeException(e);
366         }
367         finally {
368             DataAccess.cleanUp(con, ps, rs);
369         }
370     }
371 
372     private static final String _CREATE_SQL =
373         "CREATE TABLE Map (key_ BLOB not null, value_ BLOB not null, primary " +
374             "key (key_, value_))";
375 
376     private static final String _KEY = "key_";
377 
378     private static final String _VALUE = "value_";
379 
380     private String _fileName;
381 
382 }