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