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.
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.
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 found here
- You want ten different web servers, you got it.
- Extensible. You can build you own custom parsers, wrappers, channels and more.
@Expose runs well on Heroku and is battle tested on a site that has thousands of visitors per day.
@Expose consists of:
- An API
- 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
- Methods
- Arguments
- Data types
A dispatcher consists of
- A channel - Receives the incoming messages and sends the response
- A parser - Parses the incoming message
- A wrapper - Wraps the response
- Zero, one or multiple logs
The purpose of a channel is to read incoming messages and to write outgoing responses.
- 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.
The purpose of a parser is to parse incoming messages.
doSomething 123, "monkey"
doSomething?arg1=123&arg2=monkey
{"method": "doSomething", "params": {"arg1": 123, "arg2": monkey}}
The purpose of a wrapper is to wrap outgoing responses.
A simple return with comma separated values.
For wrapping responses to send to a browser. Sets HTTP response header. Handles
- Static files. Examples: HTML, CSS, image files, JavaScript files.
- POST and GET requests
Server side includes are supported for text files.
- File includes. Syntax:
<!--#include file="header.html" -->
- Variables include. Syntax:
<!--#echo var="my_var" -->
A logger has
- a type - Event or Error
- a format - formats the output
- a writer - writes the formatted output
- optionally a crypto - it can be desirable to encrypt sensitive log data
The available formats are
- JSON
- Single line format - suitable for log entries
- Multi line format - easier to read for humans
The available writers are
- System out - writes to system out
- Mail - send the entry as an emial
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.
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);
The name of the cookie
The value fo the cookie
When the cookie expires
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.
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 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.
@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.
A consumer is a dispatcher like for example the command line interface which gets its request by reading from an SQS queue.
Requests can be added to a SQS queue in any manner. There does exist two utility classes to facilitate adding messages to the queue:
JsonRpc
formats request in the expected JSON RPC format.SqsProducer
adds requests to the SQS queue.
Messages are sent in JSON-RPC 2.0 format
{"method": "doSomething", "params": {"para1": 23, "para2": 42}}
Prerequisites. An AWS SQS Fifo queue.
public class JobClass {
@Expose(requiredAccessLevel = 1,
arguments = {"Int"})
String doHeavyBackgroundJob(int count) {
Sandman.snoozeSeconds(1);
return "Phew, all done with heavy job " + count;
}
}
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();
}
//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);
- All times are in UTC.
- Where there is an option, UTF-8 is used.
2023-03-08
- Updated dependencies
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
2022-09-25
- Bug fix: At times scheduled tasks would fire twice
2022-09-04
- Added boolean as a native datatype
- Updated dependencies
2022-02-26
- ResponseCookies can now have an expires value earlier than now
- Updated dependencies
2021-10-22
- Updated dependencies
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
andJSONObject
are no longer supported IExceptionProperties
is no longer supported
- Changed:
- API requests prefixed with
api
instead ofcall
GmailEmailSender
andIEmailSender
changed their API from a single methodsend
that set all properties - for example subject and body - to an API where properties are set with setters and the methodsend
has no arguments.
- API requests prefixed with
2020-01-03
- Upgraded dependencies
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
2017-11-13
- Exposed method
startTime
. Returns time in UTC and Swedish time.
2017-11-05
- WebWrapper. Include files can contain other include files which in turn can contain other include files and so on.
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)
2017-10-19
- Added exposed method
startTime
which returns the time the instance was started.
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
andgetWebServerBuilder
. A set of factory classes has been added, for exampleCliFactory
andScheduledTaskFactory
.
2017-10-04
- Basic-utils 1.30
- Bug fix: Handles calls from browser sync
2017-09-15
- Basic-utils 1.29
2017-09-15
- Basic-utils 1.28
- Logging
- Bug fix: execution time is now reported correctly
- Bug fix:
addEventLogger
andaddErrorLogger
previously exchangedlogFormatter
andlogWriter
when exposed. logFormatter
andlogWriter
are now derived on their enum names rather than their class names.- Default logFormatter is now json
- Default logWriter is now system_out
LogFormatterFactory
andLogWriterFactory
methods changed fromgetInstance
tocreate
2017-08-08
- Basic-utils 1.26
- Scheduled Tasks
- Now supports time zones
addTask
renamed toaddMinuteTask
2017-07-12
- Can now attach properties with thrown exceptions.
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.
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.
- Uses the crypto from basic-utils instead
- Removed see-alsos annotation
- Can now expose package private methods
- More detailed error messages.
- Refactoring.