Skip to content

Commit

Permalink
Finished implementation of initial version
Browse files Browse the repository at this point in the history
Gimme gimme virtual visions!
  • Loading branch information
aggarcia3 committed Jun 8, 2020
1 parent 4f9393e commit 39ec1ae
Show file tree
Hide file tree
Showing 42 changed files with 3,700 additions and 259 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,9 @@ $RECYCLE.BIN/

# End of https://www.gitignore.io/api/vim,java,linux,macos,emacs,maven,windows,eclipse,netbeans,intellij+all,visualstudiocode

# Development configuration
sample_settings_ide.xml

# Maven package artifacts
dist/

Expand Down
16 changes: 16 additions & 0 deletions VacBotMain/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
</dependency>
<dependency>
<groupId>jakarta.ws.rs</groupId>
<artifactId>jakarta.ws.rs-api</artifactId>
</dependency>
<dependency>
<groupId>com.sun.activation</groupId>
<artifactId>jakarta.activation</artifactId>
Expand Down Expand Up @@ -70,6 +74,18 @@
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
</dependency>
<dependency>
<groupId>com.github.lalyos</groupId>
<artifactId>jfiglet</artifactId>
</dependency>
<dependency>
<groupId>com.beust</groupId>
<artifactId>jcommander</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
236 changes: 158 additions & 78 deletions VacBotMain/src/main/java/es/uvigo/esei/sing/vacbot/VacBot.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,114 +2,194 @@

package es.uvigo.esei.sing.vacbot;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;

import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.SchemaFactory;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.beust.jcommander.IValueValidator;
import com.beust.jcommander.JCommander;
import com.beust.jcommander.Parameter;
import com.beust.jcommander.ParameterException;
import com.beust.jcommander.converters.FileConverter;
import com.github.lalyos.jfiglet.FigletFont;

import es.uvigo.esei.sing.vacbot.dispatchers.TextMessageDispatcher;
import es.uvigo.esei.sing.vacbot.frontend.FrontendCommunicationException;
import es.uvigo.esei.sing.vacbot.frontend.FrontendInterface;
import es.uvigo.esei.sing.vacbot.frontend.TextMessage;
import es.uvigo.esei.sing.vacbot.frontend.telegrambot.TelegramTextMessage;
import es.uvigo.esei.sing.vacbot.responsegen.ResponseGenerator;
import es.uvigo.esei.sing.vacbot.settings.SettingsFacade;
import es.uvigo.esei.sing.vacbot.settings.SettingsLoadException;
import es.uvigo.esei.sing.vacbot.settings.VacBotSettings;
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;
import jakarta.xml.bind.ValidationEvent;
import jakarta.xml.bind.ValidationEventHandler;

/**
* The entry point of the VacBot application.
* The entry point of the VacBot application, responsible for parsing command
* line arguments, loading settings and managing the bot lifecycle.
*
* @author Alejandro González García
*/
public final class VacBot {
private static final String WELCOME_BANNER;
private static final Logger LOGGER = LoggerFactory.getLogger(VacBot.class);

@Parameter(
converter = FileConverter.class, validateValueWith = SettingsFileParameterValidator.class,
description = "settings file"
)
private File settingsFile = null;

@Parameter(
names = { "-q", "--quiet" },
description = "If specified, suppresses printing non-logging messages to the standard output and error streams."
)
private boolean quietMode = false;

@Parameter(
names = { "-h", "--help" }, help = true,
description = "Prints a usage help message."
)
private boolean showHelp = false;

static {
String bannerText;

try {
bannerText = FigletFont.convertOneLine(
VacBot.class.getResourceAsStream("/cyberlarge.flf"), VacBot.class.getSimpleName()
);
} catch (final IOException exc) {
bannerText = VacBot.class.getSimpleName() + System.lineSeparator();

LOGGER.info(
"An error occurred while generating the welcome banner text. Using a fallback string instead"
);
}

WELCOME_BANNER = bannerText;
}

/**
* Method invoked by the JVM to start application execution.
*
* @param args The command line arguments passed to the application.
*/
public static void main(final String[] args) {
try {
// TODO: move XML reading to its class and package
final Unmarshaller jaxbUnmarshaller = JAXBContext.newInstance(VacBotSettings.class).createUnmarshaller();
new VacBot().run(args);
} catch (final Exception exc) {
LOGGER.error(
"An unhandled exception has occurred. The application will now exit", exc
);

final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); // Limit resource usage
System.exit(1);
}
}

// Treat warnings during unmarshalling as errors
schemaFactory.setErrorHandler(new ErrorHandler() {
@Override
public void warning(final SAXParseException exception) throws SAXException {
throw exception;
}
/**
* Executes the main code of the application. Currently it is responsible for
* parsing the command line arguments, reading the settings and managing the
* message dispatch loop.
*
* @param args The command line arguments.
*/
private void run(final String[] args) {
// Parse the command line
final JCommander jCommander = new JCommander(this);
jCommander.parse(args);
jCommander.setProgramName(VacBot.class.getSimpleName());

if (showHelp) {
jCommander.usage();
return;
}

@Override
public void error(final SAXParseException exception) throws SAXException {
throw exception;
}
if (!quietMode) {
System.out.println(WELCOME_BANNER);
}

final TextMessageDispatcher<? extends TextMessage, ? extends Object> messageDispatcher;
try {
final InputStream settingsStream;

@Override
public void fatalError(final SAXParseException exception) throws SAXException {
throw exception;
if (settingsFile == null) {
if (!quietMode) {
System.err.println("> Reading settings from the standard input stream...");
}
});

jaxbUnmarshaller.setEventHandler(new ValidationEventHandler() {
@Override
public boolean handleEvent(final ValidationEvent event) {
return false;
settingsStream = System.in;
} else {
if (!quietMode) {
System.err.println("> Reading settings from \"" + settingsFile + "\"...");
}
});

jaxbUnmarshaller.setSchema(
SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(
VacBot.class.getResource("/settings.xsd")
)
);
settingsStream = new FileInputStream(settingsFile);
}

final VacBotSettings settings = SettingsFacade.loadFromInputStream(settingsStream);
System.err.println("> Read settings: " + settings);

ResponseGenerator.initialize(settings);

messageDispatcher = settings.getMessageDispatcherFactory().getTextMessageDispatcher(settings);
} catch (final FileNotFoundException | SettingsLoadException exc) {
if (!quietMode) {
System.err.println("! Couldn't load the application settings. The application can't start.");
}

final VacBotSettings settings = jaxbUnmarshaller.unmarshal(
new StreamSource(new FileInputStream(args[0])), VacBotSettings.class
).getValue();
LOGGER.error("Couldn't load the application settings", exc);

System.out.println("Read settings:");
System.out.println(settings);
System.out.println();
System.exit(1);
return;
} catch (final FrontendCommunicationException exc) {
if (!quietMode) {
System.err.println("! Couldn't connect to the front-end interface. The application can't start.");
}

// TODO: move this loop and make it handle different types of text messages
// with template methods
final FrontendInterface<? extends TextMessage> frontendInterface = settings.getFrontendInterfaceFactory()
.getFrontendInterface();
LOGGER.error("Couldn't connect to the front-end interface", exc);

assert frontendInterface.getTextMessageType() == TelegramTextMessage.class;
System.exit(2);
return;
}

@SuppressWarnings("unchecked") // Safe by contract
final FrontendInterface<TelegramTextMessage> textFrontendInterface = (FrontendInterface<TelegramTextMessage>) frontendInterface;
Runtime.getRuntime().addShutdownHook(new Thread("Dispatch stop thread") {
@Override
public void run() {
if (!quietMode) {
System.err.println("> Stopping...");
}

while (true) {
final TelegramTextMessage message = textFrontendInterface.awaitNextMessage();
messageDispatcher.stop();
}
});

System.out.println("Received: " + message);
// Hint the GC to kick in to take initialization trash out
System.gc();

textFrontendInterface.sendMessage(
new TelegramTextMessage(
"Hello!", Integer.MIN_VALUE, null, null, message.getChat(),
null, null, null, null, null, message.getThisMessage(),
null, null, null
)
);
// Start the message dispatch loop. This won't return
System.err.println("> Starting dispatch of incoming text messages");
messageDispatcher.dispatchUntilInterrupted();
}

/**
* Validates the settings file command line parameter.
*
* @author Alejandro González García
*/
private static final class SettingsFileParameterValidator implements IValueValidator<File> {
@Override
public void validate(final String name, final File value) throws ParameterException {
if (value != null) {
final Path path = value.toPath();
if (!Files.isRegularFile(path) || !Files.isReadable(path)) {
throw new ParameterException("The specified file is not a regular file, or is not readable");
}
}
} catch (final FileNotFoundException exc) {
exc.printStackTrace();
} catch (final JAXBException exc) {
exc.printStackTrace();
} catch (final SAXException exc) {
exc.printStackTrace();
} catch (final FrontendCommunicationException exc) {
exc.printStackTrace();
} catch (final InterruptedException exc) {
exc.printStackTrace();
}
}
}
Loading

0 comments on commit 39ec1ae

Please sign in to comment.