/* * Copyright 2012 Future Systems * * 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.krakenapps.logdb.sort; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.LinkedList; import java.util.concurrent.atomic.AtomicInteger; import org.krakenapps.codec.FastEncodingRule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; class RunOutput { private static final int WRITE_BUFFER_SIZE = 1024 * 1024 * 8; private final Logger logger = LoggerFactory.getLogger(RunOutput.class); public BufferedOutputStream dataBos; private int written; private Run run; private BufferedOutputStream indexBos; private FileOutputStream indexFos; private FileOutputStream dataFos; private byte[] intbuf = new byte[4]; private byte[] longbuf = new byte[8]; private long dataOffset; private boolean noIndexWrite; private FastEncodingRule enc = new FastEncodingRule(); public RunOutput(int id, int length, AtomicInteger cacheCount) throws IOException { this(id, length, cacheCount, false); } public RunOutput(int id, int length, AtomicInteger cacheCount, boolean noIndexWrite) throws IOException { this.noIndexWrite = noIndexWrite; int remainCacheSize = cacheCount.addAndGet(-length); if (remainCacheSize >= 0) { this.run = new Run(id, new LinkedList<Item>()); } else { cacheCount.addAndGet(length); File indexFile = null; File tmpDir = new File(System.getProperty("kraken.data.dir"), "kraken-logdb/sort"); tmpDir.mkdirs(); File dataFile = File.createTempFile("run", ".dat", tmpDir); if (!noIndexWrite) { indexFile = File.createTempFile("run", ".idx", tmpDir); logger.debug("kraken logdb: creating run output index [{}]", indexFile.getAbsolutePath()); indexFos = new FileOutputStream(indexFile); indexBos = new BufferedOutputStream(indexFos, WRITE_BUFFER_SIZE); } dataFos = new FileOutputStream(dataFile); dataBos = new BufferedOutputStream(dataFos, WRITE_BUFFER_SIZE); ReferenceCountedFile rcIndex = null; if (indexFile != null) rcIndex = new ReferenceCountedFile(indexFile.getAbsolutePath()); ReferenceCountedFile rcData = new ReferenceCountedFile(dataFile.getAbsolutePath()); this.run = new Run(id, length, rcIndex, rcData); } } public void write(Item o) throws IOException { written++; if (run.cached != null) run.cached.add(o); else { ByteBuffer buf = enc.encode(o, SortCodec.instance); int len = buf.remaining(); if (!noIndexWrite) { IoHelper.encodeLong(longbuf, dataOffset); indexBos.write(longbuf); } IoHelper.encodeInt(intbuf, len); dataBos.write(intbuf); dataBos.write(buf.array(), 0, len); buf.clear(); dataOffset += 4 + len; } } public Run finish() { ensureClose(indexBos, indexFos); ensureClose(dataBos, dataFos); run.updateLength(); return run; } private void ensureClose(BufferedOutputStream bos, FileOutputStream fos) { if (bos != null) { try { bos.close(); } catch (IOException e) { logger.error("kraken logdb: cannot close run output", e); } } if (fos != null) { try { fos.close(); } catch (IOException e) { logger.error("kraken logdb: cannot close run output", e); } } } }