/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.brooklyn.core.mgmt.persist; import java.io.File; import java.io.IOException; import java.util.Date; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.io.FileUtil; import org.apache.brooklyn.util.text.Strings; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Charsets; import com.google.common.base.Objects; import com.google.common.io.Files; /** * Reads/writes to a file. This impl does it immediately, with no synchronisation. * Callers should wrap in {@link StoreObjectAccessorLocking} if multiple threads may be accessing this. * * @author aled */ public class FileBasedStoreObjectAccessor implements PersistenceObjectStore.StoreObjectAccessor { private static final Logger LOG = LoggerFactory.getLogger(FileBasedStoreObjectAccessor.class); public FileBasedStoreObjectAccessor(File file, String tmpExtension) { this.file = file; this.tmpFile = new File(file.getParentFile(), file.getName()+(Strings.isBlank(tmpExtension) ? ".tmp" : tmpExtension)); } private final File file; private final File tmpFile; @Override public String get() { try { if (!exists()) return null; return Files.asCharSource(file, Charsets.UTF_8).read(); } catch (IOException e) { throw Exceptions.propagate("Problem reading String contents of file "+file, e); } } @Override public byte[] getBytes() { try { if (!exists()) return null; return Files.asByteSource(file).read(); } catch (IOException e) { throw Exceptions.propagate("Problem reading bytes of file "+file, e); } } @Override public boolean exists() { return file.exists(); } @Override public void put(String val) { try { if (val==null) val = ""; FileUtil.setFilePermissionsTo600(tmpFile); Files.write(val, tmpFile, Charsets.UTF_8); FileBasedObjectStore.moveFile(tmpFile, file); } catch (IOException e) { throw Exceptions.propagate("Problem writing data to file "+file+" (via temporary file "+tmpFile+")", e); } catch (InterruptedException e) { throw Exceptions.propagate(e); } } // TODO Should this write to the temporary file? Otherwise we'll risk getting a partial view of the write. @Override public void append(String val) { try { if (val==null) val = ""; FileUtil.setFilePermissionsTo600(file); Files.append(val, file, Charsets.UTF_8); } catch (IOException e) { throw Exceptions.propagate("Problem appending to file "+file, e); } } @Override public void delete() { if (!file.delete()) { if (!file.exists()) { LOG.debug("Unable to delete " + file.getAbsolutePath() + ". Probably did not exist."); } else { LOG.warn("Unable to delete " + file.getAbsolutePath() + ". Probably still locked."); } } if (tmpFile.exists() && !tmpFile.delete()) { // tmpFile is probably already deleted, so don't even log debug if it does not exist LOG.warn("Unable to delete " + tmpFile.getAbsolutePath() + ". Probably still locked."); } } @Override public Date getLastModifiedDate() { long result = file.lastModified(); if (result==0) return null; return new Date(result); } @Override public String toString() { return Objects.toStringHelper(this).add("file", file).toString(); } }