/* * 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. * * Contributions from 2013-2017 where performed either by US government * employees, or under US Veterans Health Administration contracts. * * US Veterans Health Administration contributions by government employees * are work of the U.S. Government and are not subject to copyright * protection in the United States. Portions contributed by government * employees are USGovWork (17USC ยง105). Not subject to copyright. * * Contribution by contractors to the US Veterans Health Administration * during this period are contractually contributed under the * Apache License, Version 2.0. * * See: https://www.usa.gov/government-works * * Contributions prior to 2013: * * Copyright (C) International Health Terminology Standards Development Organisation. * Licensed under the Apache License, Version 2.0. * */ package sh.isaac.provider.ibdf; //~--- JDK imports ------------------------------------------------------------ import java.io.DataOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Path; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.Semaphore; //~--- non-JDK imports -------------------------------------------------------- import org.glassfish.hk2.api.PerLookup; import org.jvnet.hk2.annotations.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sh.isaac.api.Get; import sh.isaac.api.LookupService; import sh.isaac.api.externalizable.ByteArrayDataBuffer; import sh.isaac.api.externalizable.DataWriterService; import sh.isaac.api.externalizable.OchreExternalizable; import sh.isaac.api.metacontent.MetaContentService; import sh.isaac.api.util.TimeFlushBufferedOutputStream; //~--- classes ---------------------------------------------------------------- /** * The Class BinaryDataWriterProvider. * * @author kec */ @Service(name = "ibdfWriter") @PerLookup public class BinaryDataWriterProvider implements DataWriterService { /** The Constant BUFFER_SIZE. */ private static final int BUFFER_SIZE = 1024; //~--- fields -------------------------------------------------------------- /** The logger. */ private final Logger logger = LoggerFactory.getLogger(BinaryDataWriterProvider.class); /** The pause block. */ private final Semaphore pauseBlock = new Semaphore(1); /** The buffer. */ ByteArrayDataBuffer buffer = new ByteArrayDataBuffer(BUFFER_SIZE); /** The data path. */ Path dataPath; /** The output. */ DataOutputStream output; //~--- constructors -------------------------------------------------------- /** * Instantiates a new binary data writer provider. * * @throws IOException Signals that an I/O exception has occurred. */ private BinaryDataWriterProvider() throws IOException { // for HK2 } /** * For non-HK2 use cases. * * @param dataPath the data path * @throws IOException Signals that an I/O exception has occurred. */ public BinaryDataWriterProvider(Path dataPath) throws IOException { this(); configure(dataPath); } //~--- methods ------------------------------------------------------------- /** * Close. * * @throws IOException Signals that an I/O exception has occurred. */ @Override public void close() throws IOException { try { this.output.flush(); this.output.close(); } finally { this.output = null; } } /** * Configure. * * @param path the path * @throws IOException Signals that an I/O exception has occurred. */ @Override public void configure(Path path) throws IOException { if (this.output != null) { throw new RuntimeException("Reconfiguration is not supported"); } this.dataPath = path; this.output = new DataOutputStream(new TimeFlushBufferedOutputStream(new FileOutputStream(this.dataPath.toFile(), true))); this.buffer.setExternalData(true); this.logger.info("ibdf changeset writer has been configured to write to " + this.dataPath.toAbsolutePath().toString()); if (!Get.configurationService() .inDBBuildMode()) { // record this file as already being in the database if we are in 'normal' run mode. final MetaContentService mcs = LookupService.get() .getService(MetaContentService.class); if (mcs != null) { final ConcurrentMap<String, Boolean> processedChangesets = mcs.<String, Boolean>openStore("processedChangesets"); processedChangesets.put(path.getFileName() .toString(), true); } } } /** * Flush. * * @throws IOException Signals that an I/O exception has occurred. * @see sh.isaac.api.externalizable.DataWriterService#flush() */ @Override public void flush() throws IOException { if (this.output != null) { this.output.flush(); } } /** * Pause. * * @throws IOException Signals that an I/O exception has occurred. */ @Override public void pause() throws IOException { if (this.output == null) { this.logger.warn("already paused!"); return; } this.pauseBlock.acquireUninterruptibly(); close(); this.logger.debug("ibdf writer paused"); } /** * Put. * * @param ochreObject the ochre object * @throws RuntimeException the runtime exception */ @Override public void put(OchreExternalizable ochreObject) throws RuntimeException { try { this.pauseBlock.acquireUninterruptibly(); this.buffer.clear(); ochreObject.putExternal(this.buffer); this.output.writeByte(ochreObject.getOchreObjectType() .getToken()); this.output.writeByte(ochreObject.getDataFormatVersion()); this.output.writeInt(this.buffer.getLimit()); this.output.write(this.buffer.getData(), 0, this.buffer.getLimit()); } catch (final IOException e) { throw new RuntimeException(e); } finally { this.pauseBlock.release(); } } /** * Resume. * * @throws IOException Signals that an I/O exception has occurred. */ @Override public void resume() throws IOException { if (this.pauseBlock.availablePermits() == 1) { this.logger.warn("asked to resume, but not paused?"); return; } if (this.output == null) { configure(this.dataPath); } this.pauseBlock.release(); this.logger.debug("ibdf writer resumed"); } //~--- get methods --------------------------------------------------------- /** * Gets the current path. * * @return the current path */ @Override public Path getCurrentPath() { return this.dataPath; } }