Skip to content

An easy to get started with web server that is designed to be extended

License

Notifications You must be signed in to change notification settings

Schinzel/atexpose

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@Expose

The goal of @Expose is to be the least distracting and fastest way to get a web server up and running that can serve static file content such as HTML-files and handle proprietary Ajax requests.

Put differently: It should be the web server that distracts you the least from solving the actual problem you are working on.

Maintainability Rating Coverage Security Rating Technical Debt Lines of Code

There are other frameworks and projects that offers similar functionality to @Expose. Many of them are a lot more well used and tested than @Expose. Notable examples are GlassFish, Netty and Jetty. If in doubt use these instead.

Getting started

Include the following in your POM:

<repositories>
    <repository>
        <id>maven-repo.atexpose.com</id>
        <url>https://s3-eu-west-1.amazonaws.com/maven-repo.atexpose.com/release</url>
    </repository>
</repositories>

<dependencies>
    <dependency>
        <groupId>com.atexpose</groupId>
        <artifactId>atexpose</artifactId>
        <version>1.0.[x]</version>
    </dependency>
</dependencies>

Create a sample class:

public class MyClass {

    @Expose
    public String myMethod(){
        return "My method says hello!";
    }
    
    
    @Expose(
            arguments = {"String"}
    )
    public String setTheThing(String str) {
        return "The thing was set to '" + str + "'.";
    }    
}

Start @Expose:

AtExpose.create()
	//Expose methods in a class
	.expose(new MyClass())
	//Start web server on default port 5555
	.startWebServer();

To invoke call the following URL in a browser.

http://localhost:5555/api/myMethod

Samples

Samples found here

A large degree freedom
  • You want ten different web servers, you got it.
  • Extensible. You can build you own custom parsers, wrappers, channels and more.
Battle Tested

@Expose runs well on Heroku and is battle tested on a site that has thousands of visitors per day.

Overview

@Expose consists of:

  1. An API
  2. A set of dispatcher

The API is defines the methods that can be invoked. The dispatchers are responsible for receiving incoming requests, processing the requests and sending the response.

The main components of the API are

  1. Methods
  2. Arguments
  3. Data types

A dispatcher consists of

  1. A channel - Receives the incoming messages and sends the response
  2. A parser - Parses the incoming message
  3. A wrapper - Wraps the response
  4. Zero, one or multiple logs

Object Diagram

Channels

Purpose

The purpose of a channel is to read incoming messages and to write outgoing responses.

Available Channels

  • CommandLineChannel - Reads from and writes to system out.
  • ScriptFileChannel - Input is a file with a set of requests.
  • ScheduledTask - Input is a request that is triggered at an interval.
  • ScheduledReport - Input is a request that is triggered at an interval. Output is an email sent.
  • WebChannel - Input is a request sent to a port. Output is written to the same socket from where the request originated.

Parsers

Purpose

The purpose of a parser is to parse incoming messages.

Available Parsers

TextParser

doSomething 123, "monkey"

UrlParser

doSomething?arg1=123&arg2=monkey

JsonRpcParser

{"method": "doSomething", "params": {"arg1": 123, "arg2": monkey}}

Wrappers

Purpose

The purpose of a wrapper is to wrap outgoing responses.

Available Wrappers

CsvWrapper

A simple return with comma separated values.

WebWrapper

For wrapping responses to send to a browser. Sets HTTP response header. Handles

  1. Static files. Examples: HTML, CSS, image files, JavaScript files.
  2. POST and GET requests

Server side includes are supported for text files.

  1. File includes. Syntax: <!--#include file="header.html" -->
  2. Variables include. Syntax: <!--#echo var="my_var" -->

Loggers

A logger has

  1. a type - Event or Error
  2. a format - formats the output
  3. a writer - writes the formatted output
  4. optionally a crypto - it can be desirable to encrypt sensitive log data

The available formats are

  1. JSON
  2. Single line format - suitable for log entries
  3. Multi line format - easier to read for humans

The available writers are

  1. System out - writes to system out
  2. Mail - send the entry as an emial

Generators

Purpose

The purpose of generators is to generate something from the API. For example API documentation or a Java or a JavaScript client.

The IGenerator interface

/**
 * @param methods               All the methods in the API
 * @param customDataTypeClasses All added custom data types classes added to the API
 */
void generate(List<MethodObject> methods, List<Class<?>> customDataTypeClasses);

To use a generator atExpose.generate(IGenerator)

The project includes a JsClientGenerator which generates a JavaScript client.

Cookies

When using the web server, cookies can be read and set.

To read a cookie:

String cookieValue = WebCookieHandler.getRequestCookieValue("my_cookie_name");

To write a cookie:

WebCookie cookie = WebCookie.builder()
  .name("my_cookie_name")
  .value("1234")
  .expires(Instant.now().plusSeconds(60 * 20))
  .build();
WebCookieHandler.addResponseCookie(cookie);

Cookie Attributes

Name - String

The name of the cookie

Value - String

The value fo the cookie

Expires - Instant

When the cookie expires

HttpOnly - Boolean

A cookie with the HttpOnly attribute is inaccessible to the JavaScript Document.cookie API; it's only sent to the server. For example, cookies that persist in server-side sessions don't need to be available to JavaScript and should have the HttpOnly attribute.

SameSite - SameSite Enum

The SameSite attribute lets servers specify whether/when cookies are sent with cross-site requests. This provides some protection against cross-site request forgery attacks (CSRF). It takes three possible values: Strict, Lax, and None.

Queues

Queues are a great way to reduce complexity in a system. By using message queues the different parts of the system do not get intertwined. By having a system that is decoupled it will be easier to understand, easier to change and several other benefits. You can easily have some services on AWS, some on Azure and some on your own servers.

Queues in @Expose

@Expose supports receiving requests through Amazons SQS - Simple Queue Service. To make life easier there is a stand alone utility class for adding requests to the queue.

A message queue has two main components; a producer and a consumer. A producer puts messages on the queue and a consumer consumes the messages from the queue. For @Expose messages are requests.

Consumer

A consumer is a dispatcher like for example the command line interface which gets its request by reading from an SQS queue.

Producer

Requests can be added to a SQS queue in any manner. There does exist two utility classes to facilitate adding messages to the queue:

  1. JsonRpc formats request in the expected JSON RPC format.
  2. SqsProducer adds requests to the SQS queue.

Message format

Messages are sent in JSON-RPC 2.0 format

{"method": "doSomething", "params": {"para1": 23, "para2": 42}}

Sample

Prerequisites. An AWS SQS Fifo queue.

Step 1 - On system 1 expose a method
public class JobClass {

    @Expose(requiredAccessLevel = 1,
            arguments = {"Int"})
    String doHeavyBackgroundJob(int count) {
        Sandman.snoozeSeconds(1);
        return "Phew, all done with heavy job " + count;
    }
}
Step 2 - On system 1 set up a consumer

On the system with the exposed method, start a consumer.

AtExpose.create()
	//Expose sample class
	.expose(new JobClass())
	//Start SQS consumer
	.startDispatcher(getSqsConsumer());

private static IDispatcher getSqsConsumer() {
	return SqsConsumerFactory.builder()
		.awsAccessKey(AWS.ACCESS_KEY)
		.awsSecretKey(AWS.SECRET_KEY)
		.queueUrl(AWS.QUEUE_URL)
		.region(Regions.EU_WEST_1)
		.name("MyFirstSqsConsumer")
		.noOfThreads(2)
		.accessLevel(1)
		.build();
}
Step 3 - On system 2 add requests to the queue
//Create a SqsProducer that can put messages on an AWS SQS queue.
IQueueProducer sqsProducer = SqsProducer.builder()
	.awsAccessKey(AWS.ACCESS_KEY)
	.awsSecretKey(AWS.SECRET_KEY)
	.region(Regions.EU_WEST_1)
	.queueUrl(AWS.QUEUE_URL)
	.sqsQueueType(SqsQueueType.FIFO)
	.build();

String jsonRpc = JsonRpc.builder()
	.methodName("doHeavyBackgroundJob")
	.argument("Int", String.valueOf(i))
	.build()
	.toString();
sqsProducer.send(jsonRpc);

Time and File format

  • All times are in UTC.
  • Where there is an option, UTF-8 is used.

Versions

1.0.6

2023-03-08

  • Updated dependencies

1.0.5

2022-12-03

  • Error message: removed the error class name from error message
  • Argument builder: Added allowedCharsDescription which is included in the error message when an argument does not match the reg ex pattern. It is intended to be used for a more descriptive error message for non developer users.
  • Fixed the readme section on Cookie Attributes

1.0.4

2022-09-25

  • Bug fix: At times scheduled tasks would fire twice

1.0.3

2022-09-04

  • Added boolean as a native datatype
  • Updated dependencies

1.0.2

2022-02-26

  • ResponseCookies can now have an expires value earlier than now
  • Updated dependencies

1.0.1

2021-10-22

  • Updated dependencies

1.0.0

2021-10-20

  • New features:
    • Generators
    • Read and set cookies when using the web server
    • Methods returning void are now supported
    • JavaScript client generator added. Translates the api to a JavaScript client.
    • Argument can now take an optional reg ex. All argument values has to match reg ex else an error is thrown.
    • New scheduled task that fires every hour at a given minute
  • Removed:
    • GSuite authentication is no longer supported
    • Aliases are no longer supported
    • Data types Float and JSONObject are no longer supported
    • IExceptionProperties is no longer supported
  • Changed:
    • API requests prefixed with api instead of call
    • GmailEmailSender and IEmailSender changed their API from a single method send that set all properties - for example subject and body - to an API where properties are set with setters and the method send has no arguments.

0.9.38

2020-01-03

  • Upgraded dependencies

0.9.37

2019-07-25

  • Added validation required argument count of exposed methods
  • Upgraded dependencies
  • Removed unused dependency common-codec
  • Bug fix: using unnamed arguments caused error

0.9.36

2017-11-13

  • Exposed method startTime. Returns time in UTC and Swedish time.

0.9.35

2017-11-05

  • WebWrapper. Include files can contain other include files which in turn can contain other include files and so on.

0.9.34

2017-10-31

  • Basic-utils 1.31
  • Can set custom 404 page
  • ScheduledTask/Report builder: zoneId -> timeZone
  • AtExpose. New method: start(Iterable<IDispatcher> dispatchers)
  • API. New method for more concise code: addArgument(Sring name, DataType dataType, String description)

0.9.33

2017-10-19

  • Added exposed method startTime which returns the time the instance was started.

0.9.32

2017-10-16

  • Dispatchers are built using stand alone builders instead of mainly metods or chained builders in main @Expose class. As such several methods has been removed from AtExpose, for example startCli and getWebServerBuilder. A set of factory classes has been added, for example CliFactory and ScheduledTaskFactory.

0.9.31

2017-10-04

  • Basic-utils 1.30
  • Bug fix: Handles calls from browser sync

0.9.30

2017-09-15

  • Basic-utils 1.29

0.9.29

2017-09-15

  • Basic-utils 1.28
  • Logging
    • Bug fix: execution time is now reported correctly
    • Bug fix: addEventLogger and addErrorLogger previously exchanged logFormatter and logWriter when exposed.
    • logFormatter and logWriter are now derived on their enum names rather than their class names.
    • Default logFormatter is now json
    • Default logWriter is now system_out
    • LogFormatterFactory and LogWriterFactory methods changed from getInstance to create

0.9.28

2017-08-08

  • Basic-utils 1.26
  • Scheduled Tasks
    • Now supports time zones
    • addTask renamed to addMinuteTask

0.9.27

2017-07-12

  • Can now attach properties with thrown exceptions.

0.9.26

2017-07-10

  • New basic-utils version 1.24
  • Text parser: text qualifier is now single quote instead of double quote
  • AtExpose.expose added for conciser set ups.
  • AWS Simple Queue Service supported
    • JsonRpcParser added - Parses JSON-RPC 2.0 messages.
    • SqsChannel added - Reads messages from an SQS queue.
    • Added exposed method sendToQueue(String queueProducerName, String message) that sends a message to a queue.
    • Added AtExpose.addQueueProducer(String queueProducerName, IQueueProducer queueProducer) that adds queue producers to a collection.

0.9.25

2017-06-23

  • Removed proprietary JsonOrdered.
  • Better error messages from exposed code including class, method and line number.
  • Logger encryption replaced proprietary with cipher from basic-utils.

0.9.24

  • Uses the crypto from basic-utils instead

0.9.23

  • Removed see-alsos annotation

0.9.22

  • Can now expose package private methods

0.9.21

  • More detailed error messages.

0.9.20

  • Refactoring.

About

An easy to get started with web server that is designed to be extended

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published