1d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/**
2d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *  Copyright 2011 Florian Schmaus
3d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
4d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *  Licensed under the Apache License, Version 2.0 (the "License");
5d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *  you may not use this file except in compliance with the License.
6d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *  You may obtain a copy of the License at
7d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
8d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *      http://www.apache.org/licenses/LICENSE-2.0
9d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
10d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *  Unless required by applicable law or agreed to in writing, software
11d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *  distributed under the License is distributed on an "AS IS" BASIS,
12d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *  See the License for the specific language governing permissions and
14d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *  limitations under the License.
15d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */
16d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
17d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpackage org.jivesoftware.smackx.entitycaps.cache;
18d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
19d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.DataInputStream;
20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.DataOutputStream;
21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.File;
22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.FileInputStream;
23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.FileOutputStream;
24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.IOException;
25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.Reader;
26d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.StringReader;
27d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
28d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.IQ;
29d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.provider.IQProvider;
30d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.util.Base32Encoder;
31d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.util.Base64Encoder;
32d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.util.StringEncoder;
33d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.entitycaps.EntityCapsManager;
34d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.packet.DiscoverInfo;
35d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.provider.DiscoverInfoProvider;
36d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.xmlpull.v1.XmlPullParserFactory;
37d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.xmlpull.v1.XmlPullParser;
38d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.xmlpull.v1.XmlPullParserException;
39d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
40d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/**
41d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Simple implementation of an EntityCapsPersistentCache that uses a directory
42d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * to store the Caps information for every known node. Every node is represented
43d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * by an file.
44d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
45d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @author Florian Schmaus
46d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
47d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */
48d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpublic class SimpleDirectoryPersistentCache implements EntityCapsPersistentCache {
49d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
50d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private File cacheDir;
51d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private StringEncoder filenameEncoder;
52d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
53d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
54d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Creates a new SimpleDirectoryPersistentCache Object. Make sure that the
55d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * cacheDir exists and that it's an directory.
56d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
57d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Default filename encoder {@link Base32Encoder}, as this will work on all
58d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * filesystems, both case sensitive and case insensitive.  It does however
59d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * produce longer filenames.
60d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
61d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param cacheDir
62d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
63d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public SimpleDirectoryPersistentCache(File cacheDir) {
64d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this(cacheDir, Base32Encoder.getInstance());
65d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
66d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
67d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
68d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Creates a new SimpleDirectoryPersistentCache Object. Make sure that the
69d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * cacheDir exists and that it's an directory.
70d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
71d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * If your cacheDir is case insensitive then make sure to set the
72d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * StringEncoder to {@link Base32Encoder} (which is the default).
73d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
74d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param cacheDir The directory where the cache will be stored.
75d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param filenameEncoder Encodes the node string into a filename.
76d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
77d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public SimpleDirectoryPersistentCache(File cacheDir, StringEncoder filenameEncoder) {
78d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (!cacheDir.exists())
79d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw new IllegalStateException("Cache directory \"" + cacheDir + "\" does not exist");
80d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (!cacheDir.isDirectory())
81d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw new IllegalStateException("Cache directory \"" + cacheDir + "\" is not a directory");
82d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
83d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.cacheDir = cacheDir;
84d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.filenameEncoder = filenameEncoder;
85d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
86d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
87d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    @Override
88d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void addDiscoverInfoByNodePersistent(String node, DiscoverInfo info) {
89d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String filename = filenameEncoder.encode(node);
90d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        File nodeFile = new File(cacheDir, filename);
91d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
92d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (nodeFile.createNewFile())
93d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                writeInfoToFile(nodeFile, info);
94d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (IOException e) {
95d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            e.printStackTrace();
96d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
97d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
98d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
99d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    @Override
100d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void replay() throws IOException {
101d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        File[] files = cacheDir.listFiles();
102d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        for (File f : files) {
103d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            String node = filenameEncoder.decode(f.getName());
104d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            DiscoverInfo info = restoreInfoFromFile(f);
105d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (info == null)
106d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                continue;
107d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
108d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            EntityCapsManager.addDiscoverInfoByNode(node, info);
109d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
110d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
111d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
112d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void emptyCache() {
113d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        File[] files = cacheDir.listFiles();
114d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        for (File f : files) {
115d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            f.delete();
116d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
117d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
118d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
119d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
120d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Writes the DiscoverInfo packet to an file
121d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
122d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param file
123d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param info
124d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws IOException
125d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
126d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static void writeInfoToFile(File file, DiscoverInfo info) throws IOException {
127d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        DataOutputStream dos = new DataOutputStream(new FileOutputStream(file));
128d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
129d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            dos.writeUTF(info.toXML());
130d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
131d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            dos.close();
132d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
133d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
134d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
135d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
136d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Tries to restore an DiscoverInfo packet from a file.
137d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
138d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param file
139d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return
140d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws IOException
141d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
142d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static DiscoverInfo restoreInfoFromFile(File file) throws IOException {
143d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        DataInputStream dis = new DataInputStream(new FileInputStream(file));
144d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String fileContent = null;
145d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String id;
146d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String from;
147d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String to;
148d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
149d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
150d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            fileContent = dis.readUTF();
151d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } finally {
152d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            dis.close();
153d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
154d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (fileContent == null)
155d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return null;
156d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
157d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Reader reader = new StringReader(fileContent);
158d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        XmlPullParser parser;
159d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
160d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            parser = XmlPullParserFactory.newInstance().newPullParser();
161d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
162d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            parser.setInput(reader);
163d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (XmlPullParserException xppe) {
164d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            xppe.printStackTrace();
165d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return null;
166d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
167d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
168d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        DiscoverInfo iqPacket;
169d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        IQProvider provider = new DiscoverInfoProvider();
170d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
171d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // Parse the IQ, we only need the id
172d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
173d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            parser.next();
174d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            id = parser.getAttributeValue("", "id");
175d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            from = parser.getAttributeValue("", "from");
176d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            to = parser.getAttributeValue("", "to");
177d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            parser.next();
178d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (XmlPullParserException e1) {
179d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return null;
180d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
181d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
182d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
183d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            iqPacket = (DiscoverInfo) provider.parseIQ(parser);
184d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (Exception e) {
185d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return null;
186d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
187d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
188d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        iqPacket.setPacketID(id);
189d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        iqPacket.setFrom(from);
190d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        iqPacket.setTo(to);
191d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        iqPacket.setType(IQ.Type.RESULT);
192d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return iqPacket;
193d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
194d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen}
195