001/** 002 * 003 * Copyright © 2011-2014 Florian Schmaus 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.jivesoftware.smackx.caps.cache; 018 019import java.io.DataInputStream; 020import java.io.DataOutputStream; 021import java.io.File; 022import java.io.FileInputStream; 023import java.io.FileOutputStream; 024import java.io.IOException; 025import java.util.logging.Level; 026import java.util.logging.Logger; 027 028import org.jivesoftware.smack.util.PacketParserUtils; 029import org.jivesoftware.smack.util.stringencoder.Base32; 030import org.jivesoftware.smack.util.stringencoder.StringEncoder; 031import org.jivesoftware.smackx.disco.packet.DiscoverInfo; 032 033/** 034 * Simple implementation of an EntityCapsPersistentCache that uses a directory 035 * to store the Caps information for every known node. Every node is represented 036 * by a file. 037 * 038 * @author Florian Schmaus 039 * 040 */ 041public class SimpleDirectoryPersistentCache implements EntityCapsPersistentCache { 042 private static final Logger LOGGER = Logger.getLogger(SimpleDirectoryPersistentCache.class.getName()); 043 044 private File cacheDir; 045 private StringEncoder filenameEncoder; 046 047 /** 048 * Creates a new SimpleDirectoryPersistentCache Object. Make sure that the 049 * cacheDir exists and that it's an directory. 050 * <p> 051 * Default filename encoder {@link Base32}, as this will work on all 052 * file systems, both case sensitive and case insensitive. It does however 053 * produce longer filenames. 054 * 055 * @param cacheDir 056 */ 057 public SimpleDirectoryPersistentCache(File cacheDir) { 058 this(cacheDir, Base32.getStringEncoder()); 059 } 060 061 /** 062 * Creates a new SimpleDirectoryPersistentCache Object. Make sure that the 063 * cacheDir exists and that it's an directory. 064 * 065 * If your cacheDir is case insensitive then make sure to set the 066 * StringEncoder to {@link Base32} (which is the default). 067 * 068 * @param cacheDir The directory where the cache will be stored. 069 * @param filenameEncoder Encodes the node string into a filename. 070 */ 071 public SimpleDirectoryPersistentCache(File cacheDir, StringEncoder filenameEncoder) { 072 if (!cacheDir.exists()) 073 throw new IllegalStateException("Cache directory \"" + cacheDir + "\" does not exist"); 074 if (!cacheDir.isDirectory()) 075 throw new IllegalStateException("Cache directory \"" + cacheDir + "\" is not a directory"); 076 077 this.cacheDir = cacheDir; 078 this.filenameEncoder = filenameEncoder; 079 } 080 081 @Override 082 public void addDiscoverInfoByNodePersistent(String nodeVer, DiscoverInfo info) { 083 File nodeFile = getFileFor(nodeVer); 084 try { 085 if (nodeFile.createNewFile()) 086 writeInfoToFile(nodeFile, info); 087 } catch (IOException e) { 088 LOGGER.log(Level.SEVERE, "Failed to write disco info to file", e); 089 } 090 } 091 092 @Override 093 public DiscoverInfo lookup(String nodeVer) { 094 File nodeFile = getFileFor(nodeVer); 095 if (!nodeFile.isFile()) { 096 return null; 097 } 098 DiscoverInfo info = null; 099 try { 100 info = restoreInfoFromFile(nodeFile); 101 } 102 catch (Exception e) { 103 LOGGER.log(Level.WARNING, "Coud not restore info from file", e); 104 } 105 return info; 106 } 107 108 private File getFileFor(String nodeVer) { 109 String filename = filenameEncoder.encode(nodeVer); 110 File nodeFile = new File(cacheDir, filename); 111 return nodeFile; 112 } 113 114 @Override 115 public void emptyCache() { 116 File[] files = cacheDir.listFiles(); 117 for (File f : files) { 118 f.delete(); 119 } 120 } 121 122 /** 123 * Writes the DiscoverInfo stanza(/packet) to an file 124 * 125 * @param file 126 * @param info 127 * @throws IOException 128 */ 129 private static void writeInfoToFile(File file, DiscoverInfo info) throws IOException { 130 DataOutputStream dos = new DataOutputStream(new FileOutputStream(file)); 131 try { 132 dos.writeUTF(info.toXML().toString()); 133 } finally { 134 dos.close(); 135 } 136 } 137 138 /** 139 * Tries to restore an DiscoverInfo stanza(/packet) from a file. 140 * 141 * @param file 142 * @return the restored DiscoverInfo 143 * @throws Exception 144 */ 145 private static DiscoverInfo restoreInfoFromFile(File file) throws Exception { 146 DataInputStream dis = new DataInputStream(new FileInputStream(file)); 147 String fileContent = null; 148 try { 149 fileContent = dis.readUTF(); 150 } finally { 151 dis.close(); 152 } 153 if (fileContent == null) { 154 return null; 155 } 156 DiscoverInfo info = (DiscoverInfo) PacketParserUtils.parseStanza(fileContent); 157 158 return info; 159 } 160}