001    /**
002     * Copyright (c) 2000-2012 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            public void clear() {
056                    try {
057                            _deleteDatabase();
058                            _createDatabase();
059                    }
060                    catch (Throwable t) {
061                            throw new RuntimeException(t);
062                    }
063            }
064    
065            public boolean containsKey(Object key) {
066                    int count = _getCount((K)key, null);
067    
068                    if (count > 0) {
069                            return true;
070                    }
071                    else {
072                            return false;
073                    }
074            }
075    
076            public boolean containsValue(Object value) {
077                    int count = _getCount(null, (V)value);
078    
079                    if (count > 0) {
080                            return true;
081                    }
082                    else {
083                            return false;
084                    }
085            }
086    
087            @Override
088            public Set<V> getAll(Object key) {
089                    Connection con = null;
090                    PreparedStatement ps = null;
091                    ResultSet rs = null;
092    
093                    Set<V> values = null;
094    
095                    try {
096                            con = _getConnection();
097    
098                            ps = con.prepareStatement("SELECT value_ FROM Map WHERE key_ = ?");
099    
100                            ps.setBytes(1, SerializableUtil.serialize(key));
101    
102                            rs = ps.executeQuery();
103    
104                            while (rs.next()) {
105                                    if (values == null) {
106                                            values = new HashSet<V>();
107                                    }
108    
109                                    V value = null;
110    
111                                    value = (V)SerializableUtil.deserialize(rs.getBytes(_VALUE));
112    
113                                    values.add(value);
114                            }
115                    }
116                    catch (Exception e) {
117                            throw new RuntimeException(e);
118                    }
119                    finally {
120                            DataAccess.cleanUp(con, ps, rs);
121                    }
122    
123                    return values;
124            }
125    
126            public boolean isEmpty() {
127                    int count = _getCount(null, null);
128    
129                    if (count == 0) {
130                            return true;
131                    }
132                    else {
133                            return false;
134                    }
135            }
136    
137            public Set<K> keySet() {
138                    Connection con = null;
139                    PreparedStatement ps = null;
140                    ResultSet rs = null;
141    
142                    Set<K> keys = null;
143    
144                    try {
145                            con = _getConnection();
146    
147                            ps = con.prepareStatement("SELECT key_ FROM Map");
148    
149                            rs = ps.executeQuery();
150    
151                            while (rs.next()) {
152                                    if (keys == null) {
153                                            keys = new HashSet<K>();
154                                    }
155    
156                                    K key = null;
157    
158                                    key = (K)SerializableUtil.deserialize(rs.getBytes(_KEY));
159    
160                                    keys.add(key);
161                            }
162                    }
163                    catch (Exception e) {
164                            throw new RuntimeException(e);
165                    }
166                    finally {
167                            DataAccess.cleanUp(con, ps, rs);
168                    }
169    
170                    return keys;
171            }
172    
173            public V put(K key, V value) {
174                    if ((key == null) || (value == null)) {
175                            return null;
176                    }
177    
178                    if (_getCount(key, value) == 0) {
179                            Connection con = null;
180                            PreparedStatement ps = null;
181    
182                            try {
183                                    con = _getConnection();
184    
185                                    ps = con.prepareStatement(
186                                            "INSERT INTO Map (key_, value_) values (?, ?)");
187    
188                                    ps.setBytes(1, SerializableUtil.serialize(key));
189                                    ps.setBytes(2, SerializableUtil.serialize(value));
190    
191                                    ps.execute();
192                            }
193                            catch (Exception e) {
194                                    throw new RuntimeException(e);
195                            }
196                            finally {
197                                    DataAccess.cleanUp(con, ps);
198                            }
199                    }
200    
201                    return value;
202            }
203    
204            @Override
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            @Override
275            protected void finalize() throws Throwable {
276                    try {
277                            _deleteDatabase();
278                    }
279                    finally {
280                            super.finalize();
281                    }
282            }
283    
284            private void _createDatabase() {
285                    Connection con = null;
286    
287                    try {
288                            con = _getConnection();
289    
290                            DB db = DBFactoryUtil.getDB(DB.TYPE_HYPERSONIC);
291    
292                            db.runSQL(con, _CREATE_SQL);
293                    }
294                    catch (Exception e) {
295                            throw new RuntimeException(e);
296                    }
297                    finally {
298                            DataAccess.cleanUp(con);
299                    }
300            }
301    
302            private void _deleteDatabase() throws Throwable {
303                    File[] files = new File[] {
304                            new File(_fileName + ".properties"),
305                            new File(_fileName + ".script"), new File(_fileName + ".log"),
306                            new File(_fileName + ".data"), new File(_fileName + ".backup")
307                    };
308    
309                    for (File file : files) {
310                            if (file.exists()) {
311                                    file.delete();
312                            }
313                    }
314            }
315    
316            private Connection _getConnection() throws Exception {
317                    return DriverManager.getConnection(
318                            "jdbc:hsqldb:file:" + _fileName, "sa", "");
319            }
320    
321            private int _getCount(K key, V value) {
322                    Connection con = null;
323                    PreparedStatement ps = null;
324                    ResultSet rs = null;
325    
326                    try {
327                            con = _getConnection();
328    
329                            String sql = "SELECT count(*) FROM Map ";
330    
331                            if ((key != null) && (value != null)) {
332                                    sql += "WHERE key_ = ? AND value_ = ?";
333    
334                                    ps = con.prepareStatement(sql);
335    
336                                    ps.setBytes(1, SerializableUtil.serialize(key));
337                                    ps.setBytes(2, SerializableUtil.serialize(value));
338                            }
339                            else if (key != null) {
340                                    sql += "WHERE key_ = ?";
341    
342                                    ps = con.prepareStatement(sql);
343    
344                                    ps.setBytes(1, SerializableUtil.serialize(key));
345                            }
346                            else if (value != null) {
347                                    sql += "WHERE value_ = ?";
348    
349                                    ps = con.prepareStatement(sql);
350    
351                                    ps.setBytes(1, SerializableUtil.serialize(value));
352                            }
353                            else {
354                                    ps = con.prepareStatement(sql);
355                            }
356    
357                            rs = ps.executeQuery();
358    
359                            rs.next();
360    
361                            return rs.getInt(1);
362                    }
363                    catch (Exception e) {
364                            throw new RuntimeException(e);
365                    }
366                    finally {
367                            DataAccess.cleanUp(con, ps, rs);
368                    }
369            }
370    
371            private static final String _CREATE_SQL =
372                    "CREATE TABLE Map (key_ BLOB not null, value_ BLOB not null)";
373    
374            private static final String _KEY = "key_";
375    
376            private static final String _VALUE = "value_";
377    
378            private String _fileName;
379    
380    }