Java tutorial
/*--------------------------------------------------------------*/ /* 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.corehunter.services.simple; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.UUID; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.commons.lang3.ObjectUtils; import org.corehunter.CoreHunter; import org.corehunter.CoreHunterArguments; import org.corehunter.listener.SimpleCoreHunterListener; import org.corehunter.services.CoreHunterRun; import org.corehunter.services.CoreHunterRunArguments; import org.corehunter.services.CoreHunterRunResult; import org.corehunter.services.CoreHunterRunServices; import org.corehunter.services.CoreHunterRunStatus; import org.corehunter.services.DatasetServices; import org.jamesframework.core.subset.SubsetSolution; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.XStreamException; import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; import com.thoughtworks.xstream.io.xml.StaxDriver; import uno.informatics.data.pojo.SimpleEntityPojo; /** * A simple CoreHunterRunServices implementation. Sub-classes, can use the * {@link #SimpleCoreHunterRunServices(DatasetServices) constructor} provided * path is defined in the overloaded constructor using the * {@link #setPath(Path)} method * * @author daveneti * */ public class SimpleCoreHunterRunServices implements CoreHunterRunServices { Logger logger = LoggerFactory.getLogger(SimpleCoreHunterRunServices.class); private static final String RESULTS_PATH = "RESULTS_PATH"; private DatasetServices datasetServices; private ExecutorService executor; private List<CoreHunterRun> corehunterRuns; private Map<String, CoreHunterRunResult> corehunterResultsMap; public String charsetName = "utf-8"; private boolean shuttingDown; private boolean shutDown; private Path path; /** * Constructor that can be used by sub-classes provided the path is defined * in the overloaded constructor using the {@link #setPath(Path)} method * * @param datasetServices * the dataset services in to be used by these services * @throws IOException * if the path can not be set or is invalid */ protected SimpleCoreHunterRunServices(DatasetServices datasetServices) throws IOException { this.datasetServices = datasetServices; executor = createExecutorService(); corehunterResultsMap = new HashMap<>(); } /** * Constructor that is a path to defined the location of the datasets * * @param path * the location of the datasets * @param datasetServices * the dataset services in to be used by these services * @throws IOException * if the path can not be set or is invalid */ public SimpleCoreHunterRunServices(Path path, DatasetServices datasetServices) throws IOException { this(datasetServices); setPath(path); } public final Path getPath() { return path; } public synchronized final void setPath(Path path) throws IOException { if (path == null) { throw new IOException("Path must be defined!"); } this.path = path; initialise(); } @Override public CoreHunterRun executeCoreHunter(CoreHunterRunArguments arguments) { if (shuttingDown) { throw new IllegalStateException("Can not accept any new runs, in the process of shutting down!"); } if (shutDown) { throw new IllegalStateException("Can not accept any new runs, service is not running!"); } CoreHunterRunnable corehunterRunnable = new CoreHunterRunnable(arguments); corehunterResultsMap.put(corehunterRunnable.getUniqueIdentifier(), corehunterRunnable); executor.submit(corehunterRunnable); return new CoreHunterRunFromRunnable(corehunterRunnable); } @Override public CoreHunterRun getCoreHunterRun(String uniqueIdentifier) { if (uniqueIdentifier == null) { throw new NullPointerException("Can not find a run with out an id!"); } CoreHunterRunResult coreHunterRunResult = corehunterResultsMap.get(uniqueIdentifier); if (coreHunterRunResult != null) { return new CoreHunterRunFromRunnable(coreHunterRunResult); } else { return null; } } @Override public synchronized boolean removeCoreHunterRun(String uniqueIdentifier) { if (uniqueIdentifier == null) { throw new NullPointerException("Can not remove a run with out an id!"); } CoreHunterRunResult coreHunterRunResult = corehunterResultsMap.remove(uniqueIdentifier); if (coreHunterRunResult == null) { throw new NoSuchElementException(String.format("Can not find a run with id %s", uniqueIdentifier)); } removeResult(coreHunterRunResult); // can not be stopped /*if (coreHunterRunResult instanceof CoreHunterRunnable) { boolean stopped = ((CoreHunterRunnable) coreHunterRunResult).stop(); return stopped; }*/ return true; } @Override public synchronized void deleteCoreHunterRun(String uniqueIdentifier) { if (uniqueIdentifier == null) { throw new NullPointerException("Can not delete a run with out an id!"); } // remove regardless if it can not be stopped CoreHunterRunResult coreHunterRunResult = corehunterResultsMap.remove(uniqueIdentifier); if (coreHunterRunResult == null) { throw new NoSuchElementException(String.format("Can not find a run with id %s", uniqueIdentifier)); } removeResult(coreHunterRunResult); /*if (coreHunterRunResult instanceof CoreHunterRunnable) { if (!((CoreHunterRunnable) coreHunterRunResult).stop()) { logger.error("Can not stop runnable {}", coreHunterRunResult.getName()); } }*/ } @Override public synchronized void updateCoreHunterRun(CoreHunterRun coreHunterRun) { if (coreHunterRun == null || coreHunterRun.getUniqueIdentifier() == null) { throw new NullPointerException("Can not find a run with out an id!"); } if (coreHunterRun.getName() == null) { throw new NullPointerException("Name is a required field"); } CoreHunterRunResult coreHunterRunResult = corehunterResultsMap.get(coreHunterRun.getUniqueIdentifier()); if (coreHunterRunResult == null) { throw new NoSuchElementException( String.format("Can not find a run with id %s", coreHunterRun.getUniqueIdentifier())); } if (ObjectUtils.notEqual(coreHunterRun.getName(), coreHunterRunResult.getName())) { // update and save completed runs if (coreHunterRunResult instanceof CoreHunterRunResultPojo) { ((CoreHunterRunResultPojo) coreHunterRunResult).setName(coreHunterRun.getName()); saveResult((CoreHunterRunResultPojo) coreHunterRunResult); } else { // update in memory incomplete runs if (coreHunterRunResult instanceof CoreHunterRunnable) { ((CoreHunterRunnable) coreHunterRunResult).setName(coreHunterRun.getName()); switch (coreHunterRunResult.getStatus()) { case FAILED: case FINISHED: saveResult(new CoreHunterRunResultPojo(coreHunterRunResult)); break; case NOT_STARTED: case RUNNING: default: break; } } else { throw new UnsupportedOperationException( String.format("Unknown class in use : %s", coreHunterRunResult.getClass().getName())); } } } } @Override public List<CoreHunterRun> getAllCoreHunterRuns() { // iterates through all runnables can create new CoreHunterRun objects, // which will be a snapshot // of the current status of that runnable Iterator<CoreHunterRunResult> iterator = corehunterResultsMap.values().iterator(); corehunterRuns = new ArrayList<>(corehunterResultsMap.size()); while (iterator.hasNext()) { corehunterRuns.add(new CoreHunterRunFromRunnable(iterator.next())); } return corehunterRuns; } @Override public String getOutputStream(String uniqueIdentifier) { CoreHunterRunResult corehunterRunnable = corehunterResultsMap.get(uniqueIdentifier); if (corehunterRunnable != null) { return corehunterRunnable.getOutputStream(); } else { return null; } } @Override public String getErrorStream(String uniqueIdentifier) { CoreHunterRunResult corehunterRunnable = corehunterResultsMap.get(uniqueIdentifier); if (corehunterRunnable != null) { return corehunterRunnable.getErrorStream(); } else { return null; } } @Override public String getErrorMessage(String uniqueIdentifier) { CoreHunterRunResult corehunterRunnable = corehunterResultsMap.get(uniqueIdentifier); if (corehunterRunnable != null) { return corehunterRunnable.getErrorMessage(); } else { return null; } } @Override public SubsetSolution getSubsetSolution(String uniqueIdentifier) { CoreHunterRunResult corehunterRunnable = corehunterResultsMap.get(uniqueIdentifier); if (corehunterRunnable != null) { return corehunterRunnable.getSubsetSolution(); } else { return null; } } @Override public CoreHunterRunArguments getArguments(String uniqueIdentifier) { CoreHunterRunResult corehunterRunnable = corehunterResultsMap.get(uniqueIdentifier); if (corehunterRunnable != null) { return corehunterRunnable.getArguments(); } else { return null; } } public void shutdown() { if (!shuttingDown || shutDown) { shuttingDown = true; executor.shutdown(); shutDown = true; } } private ExecutorService createExecutorService() { return Executors.newSingleThreadExecutor(); } private String createUniqueIdentifier() { return UUID.randomUUID().toString(); } private void initialise() throws IOException { Path resultsPath = Paths.get(getPath().toString(), RESULTS_PATH); if (!Files.exists(resultsPath)) { Files.createDirectories(resultsPath); } Iterator<Path> iterator = Files.list(resultsPath).iterator(); while (iterator.hasNext()) { loadResult(iterator.next()); } } private void loadResult(Path path) { try { CoreHunterRunResult result = (CoreHunterRunResult) readFromFile(path); corehunterResultsMap.put(result.getUniqueIdentifier(), result); } catch (IOException e) { logger.error("Can not load result from path {} due to {}", path, e.getMessage()); logger.error("Full error ", e); e.printStackTrace(); } } private void saveResult(CoreHunterRunResultPojo coreHunterRunResult) { Path path = Paths.get(getPath().toString(), RESULTS_PATH, coreHunterRunResult.getUniqueIdentifier()); logger.info("Writing result for {} with id {} to path {}", coreHunterRunResult.getName(), coreHunterRunResult.getUniqueIdentifier(), path.toString()); try { writeToFile(path, coreHunterRunResult); } catch (IOException e) { logger.error("Can not save result to path {} due to {}", path, e.getMessage()); logger.error("Full error ", e); e.printStackTrace(); } } private void removeResult(CoreHunterRunResult coreHunterRunResult) { Path path = Paths.get(getPath().toString(), RESULTS_PATH, coreHunterRunResult.getUniqueIdentifier()); logger.info("Removing result for {} with id {} to path {}", coreHunterRunResult.getName(), coreHunterRunResult.getUniqueIdentifier(), path.toString()); try { Files.deleteIfExists(path); } catch (IOException e) { logger.error("Can not remove result to path {} due to {}", path, e.getMessage()); logger.error("Full error ", e); e.printStackTrace(); } } /** * Reads an object from a file. The default implementation uses XStream. * Override to use another way to read objects. Must be compatible with the * {@link #writeToFile(Path, Object)} method * * @param path * the path of the file to be read * @return the object read from the file * @throws IOException * if the object can not be read from the file */ protected Object readFromFile(Path path) throws IOException { XStream xstream = createXStream(); InputStream inputStream = Files.newInputStream(path); // TODO output to temp file and then copy try { return xstream.fromXML(inputStream); } catch (XStreamException e) { e.printStackTrace(); throw new IOException(e); } } /** * Write an object to a file. The default implementation uses XStream. * Override to use another way to write objects. Must be compatible with the * {@link #readFromFile(Path)} method * * @param path * the path of the file to be written * @param object * the object to be written * @throws IOException * if the object can not be write to the file */ protected void writeToFile(Path path, Object object) throws IOException { XStream xstream = createXStream(); OutputStream outputStream; // TODO output to temp file and then copy outputStream = Files.newOutputStream(path); try { xstream.marshal(object, new PrettyPrintWriter(new OutputStreamWriter(outputStream))); } catch (XStreamException e) { throw new IOException(e); } } private XStream createXStream() { XStream xstream = new XStream(new StaxDriver()); xstream.setClassLoader(getClass().getClassLoader()); return xstream; } private class CoreHunterRunnable extends SimpleEntityPojo implements Runnable, CoreHunterRunResult { /** * */ private static final long serialVersionUID = 1L; private CoreHunterRunArguments corehunterRunArguments; private transient CoreHunter corehunter; private transient ByteArrayOutputStream outputStream; private transient ByteArrayOutputStream errorStream; private transient String errorMessage; private transient SubsetSolution subsetSolution; private Instant startInstant; private Instant endInstant; private CoreHunterRunStatus status; public CoreHunterRunnable(CoreHunterRunArguments corehunterRunArguments) { super(createUniqueIdentifier(), corehunterRunArguments.getName()); this.corehunterRunArguments = new CoreHunterRunArgumentsPojo(corehunterRunArguments); status = CoreHunterRunStatus.NOT_STARTED; outputStream = new ByteArrayOutputStream(); } /* * (non-Javadoc) * * @see * org.corehunter.services.simple.CoreHunterResult#getOutputStream() */ @Override public final String getOutputStream() { if (outputStream != null) { try { outputStream.flush(); return outputStream.toString(charsetName); } catch (UnsupportedEncodingException e) { return outputStream.toString(); } catch (IOException e) { return "Output stream can not flushed!"; } } else { return null; } } /* * (non-Javadoc) * * @see org.corehunter.services.simple.CoreHunterResult#getErrorStream() */ @Override public final String getErrorStream() { if (errorStream != null) { try { errorStream.flush(); return errorStream.toString(charsetName); } catch (UnsupportedEncodingException e) { return errorStream.toString(); } catch (IOException e) { return "Error stream can not flushed!"; } } else { return null; } } /* * (non-Javadoc) * * @see * org.corehunter.services.simple.CoreHunterResult#getErrorMessage() */ @Override public synchronized final String getErrorMessage() { return errorMessage; } /* * (non-Javadoc) * * @see * org.corehunter.services.simple.CoreHunterResult#getSubsetSolution() */ @Override public synchronized final SubsetSolution getSubsetSolution() { return subsetSolution; } /* * (non-Javadoc) * * @see * org.corehunter.services.simple.CoreHunterResult#getStartInstant() */ @Override public synchronized final Instant getStartInstant() { return startInstant; } /* * (non-Javadoc) * * @see org.corehunter.services.simple.CoreHunterResult#getEndInstant() */ @Override public synchronized final Instant getEndInstant() { return endInstant; } /* * (non-Javadoc) * * @see org.corehunter.services.simple.CoreHunterResult#getStatus() */ @Override public synchronized CoreHunterRunStatus getStatus() { return status; } /* * (non-Javadoc) * * @see org.corehunter.services.simple.CoreHunterResult#getArguments() */ @Override public final CoreHunterRunArguments getArguments() { return corehunterRunArguments; } @Override public void run() { outputStream = new ByteArrayOutputStream(); PrintStream outputPrintStream = new PrintStream(outputStream); try { startInstant = Instant.now(); status = CoreHunterRunStatus.RUNNING; outputPrintStream .println(String.format("Starting run : %s at %s", getName(), startInstant.toString())); CoreHunterArguments arguments = new CoreHunterArguments( datasetServices.getCoreHunterData(corehunterRunArguments.getDatasetId()), corehunterRunArguments.getSubsetSize(), corehunterRunArguments.getObjectives()); corehunter = new CoreHunter(); corehunter.setMaxTimeWithoutImprovement( TimeUnit.SECONDS.toMillis(corehunterRunArguments.getMaxTimeWithoutImprovement())); corehunter.setTimeLimit(TimeUnit.SECONDS.toMillis(corehunterRunArguments.getTimeLimit())); corehunter.setListener(new SimpleCoreHunterListener(outputPrintStream)); subsetSolution = corehunter.execute(arguments); status = CoreHunterRunStatus.FINISHED; } catch (Exception e) { status = CoreHunterRunStatus.FAILED; errorMessage = e.getMessage(); errorStream = new ByteArrayOutputStream(); PrintStream printStream = new PrintStream(errorStream); e.printStackTrace(printStream); outputPrintStream.println(String.format( "Error in run : %s at due to %s. See error log for more details", getName(), errorMessage)); printStream.close(); } endInstant = Instant.now(); outputPrintStream.println(String.format("Ending run : %s at %s", getName(), endInstant.toString())); saveResult(new CoreHunterRunResultPojo(this)); outputPrintStream.close(); } public boolean stop() { return false; // This simple implementation can not be stopped } } private class CoreHunterRunFromRunnable extends CoreHunterRunPojo { /** * */ private static final long serialVersionUID = 1L; public CoreHunterRunFromRunnable(CoreHunterRunResult coreHunterRunResult) { super(coreHunterRunResult.getUniqueIdentifier(), coreHunterRunResult.getName()); setStartInstant(coreHunterRunResult.getStartInstant()); setEndInstant(coreHunterRunResult.getEndInstant()); setStatus(coreHunterRunResult.getStatus()); } } }