Home | History | Annotate | Download | only in cache
      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      */
     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      */
     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
     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
    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 
    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      */
    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      */
    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