| /** |
| * Copyright 2011 Florian Schmaus |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.jivesoftware.smackx.entitycaps.cache; |
| |
| import java.io.DataInputStream; |
| import java.io.DataOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.Reader; |
| import java.io.StringReader; |
| |
| import org.jivesoftware.smack.packet.IQ; |
| import org.jivesoftware.smack.provider.IQProvider; |
| import org.jivesoftware.smack.util.Base32Encoder; |
| import org.jivesoftware.smack.util.Base64Encoder; |
| import org.jivesoftware.smack.util.StringEncoder; |
| import org.jivesoftware.smackx.entitycaps.EntityCapsManager; |
| import org.jivesoftware.smackx.packet.DiscoverInfo; |
| import org.jivesoftware.smackx.provider.DiscoverInfoProvider; |
| import org.xmlpull.v1.XmlPullParserFactory; |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| /** |
| * Simple implementation of an EntityCapsPersistentCache that uses a directory |
| * to store the Caps information for every known node. Every node is represented |
| * by an file. |
| * |
| * @author Florian Schmaus |
| * |
| */ |
| public class SimpleDirectoryPersistentCache implements EntityCapsPersistentCache { |
| |
| private File cacheDir; |
| private StringEncoder filenameEncoder; |
| |
| /** |
| * Creates a new SimpleDirectoryPersistentCache Object. Make sure that the |
| * cacheDir exists and that it's an directory. |
| * <p> |
| * Default filename encoder {@link Base32Encoder}, as this will work on all |
| * filesystems, both case sensitive and case insensitive. It does however |
| * produce longer filenames. |
| * |
| * @param cacheDir |
| */ |
| public SimpleDirectoryPersistentCache(File cacheDir) { |
| this(cacheDir, Base32Encoder.getInstance()); |
| } |
| |
| /** |
| * Creates a new SimpleDirectoryPersistentCache Object. Make sure that the |
| * cacheDir exists and that it's an directory. |
| * |
| * If your cacheDir is case insensitive then make sure to set the |
| * StringEncoder to {@link Base32Encoder} (which is the default). |
| * |
| * @param cacheDir The directory where the cache will be stored. |
| * @param filenameEncoder Encodes the node string into a filename. |
| */ |
| public SimpleDirectoryPersistentCache(File cacheDir, StringEncoder filenameEncoder) { |
| if (!cacheDir.exists()) |
| throw new IllegalStateException("Cache directory \"" + cacheDir + "\" does not exist"); |
| if (!cacheDir.isDirectory()) |
| throw new IllegalStateException("Cache directory \"" + cacheDir + "\" is not a directory"); |
| |
| this.cacheDir = cacheDir; |
| this.filenameEncoder = filenameEncoder; |
| } |
| |
| @Override |
| public void addDiscoverInfoByNodePersistent(String node, DiscoverInfo info) { |
| String filename = filenameEncoder.encode(node); |
| File nodeFile = new File(cacheDir, filename); |
| try { |
| if (nodeFile.createNewFile()) |
| writeInfoToFile(nodeFile, info); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| @Override |
| public void replay() throws IOException { |
| File[] files = cacheDir.listFiles(); |
| for (File f : files) { |
| String node = filenameEncoder.decode(f.getName()); |
| DiscoverInfo info = restoreInfoFromFile(f); |
| if (info == null) |
| continue; |
| |
| EntityCapsManager.addDiscoverInfoByNode(node, info); |
| } |
| } |
| |
| public void emptyCache() { |
| File[] files = cacheDir.listFiles(); |
| for (File f : files) { |
| f.delete(); |
| } |
| } |
| |
| /** |
| * Writes the DiscoverInfo packet to an file |
| * |
| * @param file |
| * @param info |
| * @throws IOException |
| */ |
| private static void writeInfoToFile(File file, DiscoverInfo info) throws IOException { |
| DataOutputStream dos = new DataOutputStream(new FileOutputStream(file)); |
| try { |
| dos.writeUTF(info.toXML()); |
| } finally { |
| dos.close(); |
| } |
| } |
| |
| /** |
| * Tries to restore an DiscoverInfo packet from a file. |
| * |
| * @param file |
| * @return |
| * @throws IOException |
| */ |
| private static DiscoverInfo restoreInfoFromFile(File file) throws IOException { |
| DataInputStream dis = new DataInputStream(new FileInputStream(file)); |
| String fileContent = null; |
| String id; |
| String from; |
| String to; |
| |
| try { |
| fileContent = dis.readUTF(); |
| } finally { |
| dis.close(); |
| } |
| if (fileContent == null) |
| return null; |
| |
| Reader reader = new StringReader(fileContent); |
| XmlPullParser parser; |
| try { |
| parser = XmlPullParserFactory.newInstance().newPullParser(); |
| parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); |
| parser.setInput(reader); |
| } catch (XmlPullParserException xppe) { |
| xppe.printStackTrace(); |
| return null; |
| } |
| |
| DiscoverInfo iqPacket; |
| IQProvider provider = new DiscoverInfoProvider(); |
| |
| // Parse the IQ, we only need the id |
| try { |
| parser.next(); |
| id = parser.getAttributeValue("", "id"); |
| from = parser.getAttributeValue("", "from"); |
| to = parser.getAttributeValue("", "to"); |
| parser.next(); |
| } catch (XmlPullParserException e1) { |
| return null; |
| } |
| |
| try { |
| iqPacket = (DiscoverInfo) provider.parseIQ(parser); |
| } catch (Exception e) { |
| return null; |
| } |
| |
| iqPacket.setPacketID(id); |
| iqPacket.setFrom(from); |
| iqPacket.setTo(to); |
| iqPacket.setType(IQ.Type.RESULT); |
| return iqPacket; |
| } |
| } |