001    /**
002     * Copyright (c) 2000-2013 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.util;
016    
017    import com.liferay.portal.kernel.dao.db.DB;
018    import com.liferay.portal.kernel.dao.db.DBFactoryUtil;
019    import com.liferay.portal.kernel.dao.jdbc.DataAccess;
020    import com.liferay.portal.kernel.util.FileUtil;
021    import com.liferay.portal.kernel.util.MultiValueMap;
022    import com.liferay.util.SerializableUtil;
023    
024    import java.io.File;
025    import java.io.Serializable;
026    
027    import java.sql.Connection;
028    import java.sql.DriverManager;
029    import java.sql.PreparedStatement;
030    import java.sql.ResultSet;
031    
032    import java.util.Collection;
033    import java.util.HashSet;
034    import java.util.Set;
035    
036    /**
037     * @author Alexander Chow
038     */
039    public class FileMultiValueMap<K extends Serializable, V extends Serializable>
040            extends MultiValueMap<K, V> {
041    
042            public FileMultiValueMap() {
043                    _fileName = FileUtil.createTempFileName();
044    
045                    try {
046                            Class.forName("org.hsqldb.jdbcDriver");
047                    }
048                    catch (Exception e) {
049                            throw new RuntimeException(e);
050                    }
051    
052                    _createDatabase();
053            }
054    
055            @Override
056            public void clear() {
057                    try {
058                            _deleteDatabase();
059                            _createDatabase();
060                    }
061                    catch (Throwable t) {
062                            throw new RuntimeException(t);
063                    }
064            }
065    
066            @Override
067            public boolean containsKey(Object key) {
068                    int count = _getCount((K)key, null);
069    
070                    if (count > 0) {
071                            return true;
072                    }
073                    else {
074                            return false;
075                    }
076            }
077    
078            @Override
079            public boolean containsValue(Object value) {
080                    int count = _getCount(null, (V)value);
081    
082                    if (count > 0) {
083                            return true;
084                    }
085                    else {
086                            return false;
087                    }
088            }
089    
090            @Override
091            public Set<V> getAll(Object key) {
092                    Connection con = null;
093                    PreparedStatement ps = null;
094                    ResultSet rs = null;
095    
096                    Set<V> values = null;
097    
098                    try {
099                            con = _getConnection();
100    
101                            ps = con.prepareStatement("SELECT value_ FROM Map WHERE key_ = ?");
102    
103                            ps.setBytes(1, SerializableUtil.serialize(key));
104    
105                            rs = ps.executeQuery();
106    
107                            while (rs.next()) {
108                                    if (values == null) {
109                                            values = new HashSet<V>();
110                                    }
111    
112                                    V value = null;
113    
114                                    value = (V)SerializableUtil.deserialize(rs.getBytes(_VALUE));
115    
116                                    values.add(value);
117                            }
118                    }
119                    catch (Exception e) {
120                            throw new RuntimeException(e);
121                    }
122                    finally {
123                            DataAccess.cleanUp(con, ps, rs);
124                    }
125    
126                    return values;
127            }
128    
129            @Override
130            public boolean isEmpty() {
131                    int count = _getCount(null, null);
132    
133                    if (count == 0) {
134                            return true;
135                    }
136                    else {
137                            return false;
138                    }
139            }
140    
141            @Override
142            public Set<K> keySet() {
143                    Connection con = null;
144                    PreparedStatement ps = null;
145                    ResultSet rs = null;
146    
147                    Set<K> keys = null;
148    
149                    try {
150                            con = _getConnection();
151    
152                            ps = con.prepareStatement("SELECT key_ FROM Map");
153    
154                            rs = ps.executeQuery();
155    
156                            while (rs.next()) {
157                                    if (keys == null) {
158                                            keys = new HashSet<K>();
159                                    }
160    
161                                    K key = null;
162    
163                                    key = (K)SerializableUtil.deserialize(rs.getBytes(_KEY));
164    
165                                    keys.add(key);
166                            }
167                    }
168                    catch (Exception e) {
169                            throw new RuntimeException(e);
170                    }
171                    finally {
172                            DataAccess.cleanUp(con, ps, rs);
173                    }
174    
175                    return keys;
176            }
177    
178            @Override
179            public V put(K key, V value) {
180                    if ((key == null) || (value == null)) {
181                            return null;
182                    }
183    
184                    if (_getCount(key, value) == 0) {
185                            Connection con = null;
186                            PreparedStatement ps = null;
187    
188                            try {
189                                    con = _getConnection();
190    
191                                    ps = con.prepareStatement(
192                                            "INSERT INTO Map (key_, value_) values (?, ?)");
193    
194                                    ps.setBytes(1, SerializableUtil.serialize(key));
195                                    ps.setBytes(2, SerializableUtil.serialize(value));
196    
197                                    ps.execute();
198                            }
199                            catch (Exception e) {
200                                    throw new RuntimeException(e);
201                            }
202                            finally {
203                                    DataAccess.cleanUp(con, ps);
204                            }
205                    }
206    
207                    return value;
208            }
209    
210            @Override
211            public Set<V> putAll(K key, Collection<? extends V> values) {
212                    Set<V> curValues = getAll(key);
213    
214                    if ((values == null) || values.isEmpty()) {
215                            return curValues;
216                    }
217    
218                    if (curValues == null) {
219                            values = new HashSet<V>();
220                    }
221    
222                    for (V value : values) {
223                            if (!curValues.contains(value)) {
224                                    curValues.add(value);
225    
226                                    put(key, value);
227                            }
228                    }
229    
230                    return curValues;
231            }
232    
233            @Override
234            public V remove(Object key) {
235                    Connection con = null;
236                    PreparedStatement ps = null;
237                    ResultSet rs = null;
238    
239                    V firstValue = null;
240    
241                    try {
242                            con = _getConnection();
243    
244                            ps = con.prepareStatement("SELECT value_ FROM Map WHERE key_ = ?");
245    
246                            ps.setBytes(1, SerializableUtil.serialize(key));
247    
248                            rs = ps.executeQuery();
249    
250                            if (rs.next()) {
251                                    firstValue = (V)SerializableUtil.deserialize(
252                                            rs.getBytes(_VALUE));
253                            }
254                    }
255                    catch (Exception e) {
256                            throw new RuntimeException(e);
257                    }
258                    finally {
259                            DataAccess.cleanUp(con, ps, rs);
260                    }
261    
262                    try {
263                            con = _getConnection();
264    
265                            ps = con.prepareStatement("DELETE FROM Map WHERE key_ = ?");
266    
267                            ps.setBytes(1, SerializableUtil.serialize(key));
268    
269                            ps.execute();
270                    }
271                    catch (Exception e) {
272                            throw new RuntimeException(e);
273                    }
274                    finally {
275                            DataAccess.cleanUp(con, ps);
276                    }
277    
278                    return firstValue;
279            }
280    
281            @Override
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                            DB db = DBFactoryUtil.getDB(DB.TYPE_HYPERSONIC);
298    
299                            db.runSQL(con, _CREATE_SQL);
300                    }
301                    catch (Exception e) {
302                            throw new RuntimeException(e);
303                    }
304                    finally {
305                            DataAccess.cleanUp(con);
306                    }
307            }
308    
309            private void _deleteDatabase() throws Throwable {
310                    File[] files = new File[] {
311                            new File(_fileName + ".properties"),
312                            new File(_fileName + ".script"), new File(_fileName + ".log"),
313                            new File(_fileName + ".data"), 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)";
380    
381            private static final String _KEY = "key_";
382    
383            private static final String _VALUE = "value_";
384    
385            private String _fileName;
386    
387    }