/* * 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.commit; //~--- JDK imports ------------------------------------------------------------ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Optional; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; //~--- non-JDK imports -------------------------------------------------------- import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.glassfish.hk2.runlevel.RunLevel; import org.jvnet.hk2.annotations.Service; import sh.isaac.api.ConfigurationService; import sh.isaac.api.Get; import sh.isaac.api.LookupService; import sh.isaac.api.SystemStatusService; import sh.isaac.api.collections.ConceptSequenceSet; import sh.isaac.api.collections.SememeSequenceSet; import sh.isaac.api.commit.ChangeSetListener; import sh.isaac.api.commit.ChangeSetWriterService; import sh.isaac.api.commit.CommitRecord; import sh.isaac.api.component.concept.ConceptChronology; import sh.isaac.api.component.concept.ConceptVersion; import sh.isaac.api.component.sememe.SememeChronology; import sh.isaac.api.component.sememe.version.SememeVersion; import sh.isaac.api.externalizable.DataWriterService; import sh.isaac.api.externalizable.MultipleDataWriterService; import sh.isaac.api.externalizable.OchreExternalizable; import sh.isaac.api.util.NamedThreadFactory; //~--- classes ---------------------------------------------------------------- /** * {@link ChangeSetWriterHandler}. * * @author <a href="mailto:nmarques@westcoastinformatics.com">Nuno Marques</a> */ @Service(name = "Change Set Writer Handler") @RunLevel(value = 4) public class ChangeSetWriterHandler implements ChangeSetWriterService, ChangeSetListener { /** The Constant LOG. */ private static final Logger LOG = LogManager.getLogger(); /** The Constant jsonFileSuffix. */ private static final String jsonFileSuffix = "json"; /** The Constant ibdfFileSuffix. */ private static final String ibdfFileSuffix = "ibdf"; /** The Constant CHANGESETS. */ private static final String CHANGESETS = "changesets"; //~--- fields -------------------------------------------------------------- /** The change set writer handler uuid. */ private final UUID changeSetWriterHandlerUuid = UUID.randomUUID(); /** The writer. */ private DataWriterService writer; /** The change set write executor. */ private ExecutorService changeSetWriteExecutor; /** The write enabled. */ private boolean writeEnabled; /** The db build mode. */ private Boolean dbBuildMode; /** The change set folder. */ private final Path changeSetFolder; //~--- constructors -------------------------------------------------------- /** * Instantiates a new change set writer handler. * * @throws Exception the exception */ public ChangeSetWriterHandler() throws Exception { final Optional<Path> databasePath = LookupService.getService(ConfigurationService.class) .getDataStoreFolderPath(); this.changeSetFolder = databasePath.get() .resolve(CHANGESETS); Files.createDirectories(this.changeSetFolder); if (!this.changeSetFolder.toFile() .isDirectory()) { throw new RuntimeException("Cannot initialize Changeset Store - was unable to create " + this.changeSetFolder.toAbsolutePath()); } this.writer = new MultipleDataWriterService(this.changeSetFolder, "ChangeSet-", Optional.of(jsonFileSuffix), Optional.of(ibdfFileSuffix)); } //~--- methods ------------------------------------------------------------- /** * Disable. */ @Override public void disable() { this.writeEnabled = false; } /** * Enable. */ @Override public void enable() { this.writeEnabled = true; } /** * Handle post commit. * * @param commitRecord the commit record */ @Override public void handlePostCommit(CommitRecord commitRecord) { LOG.info("handle Post Commit"); if (this.dbBuildMode == null) { this.dbBuildMode = Get.configurationService() .inDBBuildMode(); if (this.dbBuildMode) { stopMe(); } } if (this.writeEnabled &&!this.dbBuildMode) { // Do in the backgound final Runnable r = () -> { try { if ((commitRecord.getConceptsInCommit() != null) && (commitRecord.getConceptsInCommit().size() > 0)) { sequenceSetChange(commitRecord.getConceptsInCommit()); LOG.debug("handle Post Commit: {} concepts", commitRecord.getConceptsInCommit() .size()); } if ((commitRecord.getSememesInCommit() != null) && (commitRecord.getSememesInCommit().size() > 0)) { sequenceSetChange(commitRecord.getSememesInCommit()); LOG.debug("handle Post Commit: {} sememes", commitRecord.getSememesInCommit() .size()); } } catch (final Exception e) { LOG.error("Error in Change set writer handler ", e.getMessage()); throw new RuntimeException(e); } }; this.changeSetWriteExecutor.execute(r); } else { LOG.info("ChangeSetWriter ignoring commit"); } } /** * Pause. * * @throws IOException Signals that an I/O exception has occurred. */ @Override public void pause() throws IOException { if (this.writer != null) { this.writer.pause(); } } /** * Resume. * * @throws IOException Signals that an I/O exception has occurred. */ @Override public void resume() throws IOException { if (this.writer != null) { this.writer.resume(); } } /** * Sequence set change. * * @param conceptSequenceSet the concept sequence set */ /* */ private void sequenceSetChange(ConceptSequenceSet conceptSequenceSet) { conceptSequenceSet.stream().forEach((conceptSequence) -> { final ConceptChronology<? extends ConceptVersion<?>> concept = Get.conceptService() .getConcept( conceptSequence); try { writeToFile(concept); } catch (final IOException e) { throw new RuntimeException(e); } }); } /** * Sequence set change. * * @param sememeSequenceSet the sememe sequence set */ /* */ private void sequenceSetChange(SememeSequenceSet sememeSequenceSet) { sememeSequenceSet.stream().forEach((sememeSequence) -> { final SememeChronology<? extends SememeVersion<?>> sememe = Get.sememeService() .getSememe( sememeSequence); try { writeToFile(sememe); } catch (final IOException e) { throw new RuntimeException(e); } }); } /** * Start me. */ @PostConstruct private void startMe() { try { LOG.info("Starting ChangeSetWriterHandler post-construct"); enable(); this.changeSetWriteExecutor = Executors.newSingleThreadExecutor(new NamedThreadFactory("ISAAC-changeset-write", false)); Get.postCommitService() .addChangeSetListener(this); } catch (final Exception e) { LOG.error("Error in ChangeSetWriterHandler post-construct ", e); LookupService.getService(SystemStatusService.class) .notifyServiceConfigurationFailure("Change Set Writer Handler", e); throw new RuntimeException(e); } } /** * Stop me. */ @PreDestroy private void stopMe() { LOG.info("Stopping ChangeSetWriterHandler pre-destroy"); disable(); if (this.changeSetWriteExecutor != null) { this.changeSetWriteExecutor.shutdown(); this.changeSetWriteExecutor = null; } if (this.writer != null) { LOG.debug("Close writer"); try { this.writer.close(); } catch (final IOException e) { LOG.error("Error closing changeset writer!", e); } finally { this.writer = null; } } } /** * Write to file. * * @param ochreObject the ochre object * @throws IOException Signals that an I/O exception has occurred. */ private void writeToFile(OchreExternalizable ochreObject) throws IOException { this.writer.put(ochreObject); } //~--- get methods --------------------------------------------------------- /** * Gets the listener uuid. * * @return the listener uuid */ @Override public UUID getListenerUuid() { return this.changeSetWriterHandlerUuid; } /** * Gets the write folder. * * @return the write folder */ @Override public Path getWriteFolder() { return this.changeSetFolder; } /** * Gets the write status. * * @return the write status */ @Override public boolean getWriteStatus() { return this.writeEnabled; } }