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