001/**
002 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016package org.javamoney.moneta.loader.internal;
017
018import java.io.*;
019import java.util.Map;
020import java.util.Objects;
021import java.util.concurrent.ConcurrentHashMap;
022import java.util.logging.Level;
023import java.util.logging.Logger;
024
025/**
026 * Default implementation of {@link ResourceCache}, using the local file system.
027 *
028 * @author Anatole Tresch
029 */
030public class DefaultResourceCache implements ResourceCache{
031    /**
032     * The logger used.
033     */
034    private static final Logger LOG = Logger.getLogger(DefaultResourceCache.class.getName());
035    /**
036     * Suffix for files created.
037     */
038    private static final String SUFFIX = ".dat";
039    /**
040     * Local temp directory.
041     */
042    private File localDir = new File(System.getProperty("temp.dir", ".resourceCache"));
043    /**
044     * Cached resources.
045     */
046    private Map<String,File> cachedResources = new ConcurrentHashMap<>();
047
048    /**
049     * Constructor.
050     */
051    public DefaultResourceCache(){
052        if(!localDir.exists()){
053            if(!localDir.mkdirs()){
054                LOG.severe("Error creating cache dir  " + localDir + ", resource cache disabled!");
055                localDir = null;
056            }else{
057                LOG.finest("Created cache dir  " + localDir);
058            }
059        }else if(!localDir.isDirectory()){
060            LOG.severe("Error initializing cache dir  " + localDir + ", not a directory, resource cache disabled!");
061            localDir = null;
062        }else if(!localDir.canWrite()){
063            LOG.severe("Error initializing cache dir  " + localDir + ", not writable, resource cache disabled!");
064            localDir = null;
065        }
066        if(Objects.nonNull(localDir)){
067            File[] files = localDir.listFiles();
068            if(files != null){
069                for(File file : files){
070                    if(file.isFile()){
071                        String resourceId = file.getName().substring(0, file.getName().length() - 4);
072                        cachedResources.put(resourceId, file);
073                    }
074                }
075            }
076        }
077    }
078
079    /*
080     * (non-Javadoc)
081     *
082     * @see
083     * org.javamoney.moneta.loader.internal.ResourceCache#write(java.lang.String
084     * , byte[])
085     */
086    @Override
087    public void write(String resourceId, byte[] data){
088        try{
089            File f = this.cachedResources.get(resourceId);
090            if(Objects.isNull(f)){
091                f = new File(localDir, resourceId + SUFFIX);
092                writeFile(f, data);
093                this.cachedResources.put(resourceId, f);
094            }else{
095                writeFile(f, data);
096            }
097        }
098        catch(Exception e){
099            LOG.log(Level.WARNING, "Caching of resource failed: " + resourceId, e);
100        }
101    }
102
103    /**
104     * Writees a file with the given data,
105     *
106     * @param f    the file
107     * @param data the data
108     * @throws IOException if writing failed.
109     */
110    private void writeFile(File f, byte[] data) throws IOException{
111        BufferedOutputStream bos = null;
112        try{
113            bos = new BufferedOutputStream(new FileOutputStream(f));
114            bos.write(data);
115            bos.flush();
116        }
117        finally{
118            try{
119                if(Objects.nonNull(bos)){
120                    bos.close();
121                }
122            }
123            catch(Exception e2){
124                LOG.log(Level.SEVERE, "Error closing output stream for " + f, e2);
125            }
126        }
127
128    }
129
130    /*
131     * (non-Javadoc)
132     *
133     * @see
134     * org.javamoney.moneta.loader.internal.ResourceCache#isCached(java.lang
135     * .String)
136     */
137    @Override
138    public boolean isCached(String resourceId){
139        return this.cachedResources.containsKey(resourceId);
140    }
141
142    /*
143     * (non-Javadoc)
144     *
145     * @see
146     * org.javamoney.moneta.loader.internal.ResourceCache#read(java.lang.String)
147     */
148    @Override
149    public byte[] read(String resourceId){
150        File f = this.cachedResources.get(resourceId);
151        if(Objects.isNull(f)){
152            return null;
153        }
154        return readFile(f);
155    }
156
157    @Override
158    public void clear(String resourceId){
159        File f = this.cachedResources.get(resourceId);
160        if(f != null){
161            if(f.exists()){
162                if(!f.delete()){
163                    LOG.warning("Failed to delete caching file: " + f.getAbsolutePath());
164                }
165            }
166            this.cachedResources.remove(resourceId);
167        }
168    }
169
170    /**
171     * Read a file.
172     *
173     * @param f the file
174     * @return the bytes read.
175     */
176    private byte[] readFile(File f){
177        ByteArrayOutputStream bos = new ByteArrayOutputStream();
178        BufferedInputStream is = null;
179        try{
180            is = new BufferedInputStream(new FileInputStream(f));
181            byte[] input = new byte[1024];
182            int read = 1;
183            while(read > 0){
184                read = is.read(input);
185                if(read > 0){
186                    bos.write(input, 0, read);
187                }
188            }
189            return bos.toByteArray();
190        }
191        catch(Exception e){
192            LOG.log(Level.SEVERE, "Error reading cached resource from " + f, e);
193            return null;
194        }
195        finally{
196            try{
197                if(Objects.nonNull(is)){
198                    is.close();
199                }
200            }
201            catch(Exception e2){
202                LOG.log(Level.SEVERE, "Error closing input stream from " + f, e2);
203            }
204        }
205/*
206URI fileUri = this.cachedResource;
207        if(fileUri == null){
208            String userHome = System.getProperty("user.home");
209            File file = new File(userHome + "/.cache", resourceId);
210            if(file.exists()){
211                fileUri = file.toURI();
212            }
213        }
214        if(fileUri != null){
215            File file = new File(fileUri);
216            try(
217                    FileInputStream fis = new FileInputStream(file);
218                    BufferedInputStream bis = new BufferedInputStream(fis);
219                    ObjectInputStream ois = new ObjectInputStream(bis)
220            ){
221                long loadTS = ois.readLong();
222                byte[] data = (byte[]) ois.readObject();
223                this.lastLoaded = loadTS;
224                setData(data);
225                return true;
226            }
227            catch(Exception e){
228                LOG.log(Level.WARNING, "Failed to read data from cache: " + fileUri, e);
229            }
230        }
231        return false;
232 */
233    }
234
235    @Override
236    public String toString(){
237        return "DefaultResourceCache [localDir=" + localDir + ", cachedResources=" + cachedResources + "]";
238    }
239
240}