/** * personium.io * Copyright 2014 FUJITSU LIMITED * * 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 com.fujitsu.dc.core.model.file; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.StreamingOutput; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fujitsu.dc.core.DcCoreConfig; /** * Davファイルの内容を Responseに返却する際に利用する StreamingOutputクラス. 内部的には、読み込み専用にハードリンクを作成し、出力完了後に削除する。 */ public class StreamingOutputForDavFile implements StreamingOutput { private static Logger logger = LoggerFactory.getLogger(StreamingOutputForDavFile.class); /** * Davファイルの読み書き時、ハードリンク作成/ファイル名改変時の最大リトライ回数. */ private static int maxRetryCount = DcCoreConfig.getDavFileOperationRetryCount(); /** * Davファイルの読み書き時、ハードリンク作成/ファイル名改変時のリトライ間隔(msec). */ private static long retryInterval = DcCoreConfig.getDavFileOperationRetryInterval(); /** * 読み込み用のハードリンクのパス. */ Path hardLinkPath = null; /** * 読み込み用のハードリンクからの入力ストリーム. */ InputStream hardLinkInput = null; /** * コンストラクタ. * @param fileFullPath 読み込むファイルのフルパス * @throws BinaryDataNotFoundException ファイルが存在しない場合. */ public StreamingOutputForDavFile(String fileFullPath) throws BinaryDataNotFoundException { if (!Files.exists(Paths.get(fileFullPath))) { throw new BinaryDataNotFoundException(fileFullPath); } // 読み込み専用のハードリンクを作成するため、ユニーク名を生成。 String hardLinkName = UniqueNameComposer.compose(fileFullPath); for (int i = 0; i < maxRetryCount; i++) { try { synchronized (fileFullPath) { // ハードリンクを作成. hardLinkPath = Files.createLink(Paths.get(hardLinkName), Paths.get(fileFullPath)); } // ハードリンクからの入力ストリームを取得 hardLinkInput = new BufferedInputStream(new FileInputStream(hardLinkPath.toFile())); // 成功したら終了 return; } catch (IOException e) { // 指定回数まではリトライする。 logger.debug(String.format("Creating hard link %s failed. Will try again.", hardLinkName)); try { Thread.sleep(retryInterval); } catch (InterruptedException e1) { logger.debug("Thread interrupted."); } } } throw new BinaryDataNotFoundException("Unable to create hard link for DAV file: " + hardLinkName); } @Override public void write(OutputStream output) throws IOException, WebApplicationException { if (null == hardLinkInput) { throw new WebApplicationException(new BinaryDataNotFoundException(hardLinkPath.toString())); } try { IOUtils.copy(hardLinkInput, output); } finally { IOUtils.closeQuietly(hardLinkInput); // 後始末。自分用の読み込みハードリンクを削除する。 Files.delete(hardLinkPath); } } }