/** * * Copyright © 2011-2019 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.caps.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.util.logging.Level; import java.util.logging.Logger; import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.util.stringencoder.Base32; import org.jivesoftware.smack.util.stringencoder.StringEncoder; import org.jivesoftware.smackx.disco.packet.DiscoverInfo; /** * Simple implementation of an EntityCapsPersistentCache that uses a directory * to store the Caps information for every known node. Every node is represented * by a file. * * @author Florian Schmaus * */ public class SimpleDirectoryPersistentCache implements EntityCapsPersistentCache { private static final Logger LOGGER = Logger.getLogger(SimpleDirectoryPersistentCache.class.getName()); private final File cacheDir; private final StringEncoder filenameEncoder; /** * Creates a new SimpleDirectoryPersistentCache Object. Make sure that the * cacheDir exists and that it's an directory. *

* Default filename encoder {@link Base32}, as this will work on all * file systems, both case sensitive and case insensitive. It does however * produce longer filenames. * * @param cacheDir TODO javadoc me please */ public SimpleDirectoryPersistentCache(File cacheDir) { this(cacheDir, Base32.getStringEncoder()); } /** * 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 Base32} (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 nodeVer, DiscoverInfo info) { File nodeFile = getFileFor(nodeVer); try { if (nodeFile.createNewFile()) writeInfoToFile(nodeFile, info); } catch (IOException e) { LOGGER.log(Level.SEVERE, "Failed to write disco info to file", e); } } @Override public DiscoverInfo lookup(String nodeVer) { File nodeFile = getFileFor(nodeVer); if (!nodeFile.isFile()) { return null; } DiscoverInfo info = null; try { info = restoreInfoFromFile(nodeFile); } catch (Exception e) { LOGGER.log(Level.WARNING, "Coud not restore info from file", e); } return info; } private File getFileFor(String nodeVer) { String filename = filenameEncoder.encode(nodeVer); return new File(cacheDir, filename); } @Override public void emptyCache() { File[] files = cacheDir.listFiles(); if (files == null) { return; } for (File f : files) { f.delete(); } } /** * Writes the DiscoverInfo stanza to an file * * @param file TODO javadoc me please * @param info TODO javadoc me please * @throws IOException if an I/O error occurred. */ private static void writeInfoToFile(File file, DiscoverInfo info) throws IOException { try (DataOutputStream dos = new DataOutputStream(new FileOutputStream(file))) { dos.writeUTF(info.toXML().toString()); } } /** * Tries to restore an DiscoverInfo stanza from a file. * * @param file TODO javadoc me please * @return the restored DiscoverInfo * @throws Exception if an exception occurs. */ private static DiscoverInfo restoreInfoFromFile(File file) throws Exception { String fileContent; try (DataInputStream dis = new DataInputStream(new FileInputStream(file))) { fileContent = dis.readUTF(); } if (fileContent == null) { return null; } return PacketParserUtils.parseStanza(fileContent); } }