SFS2X Docs / AdvancedTopics / server-side-extensions
» Server-side Extensions
SmartFoxServer 2X Extensions have been revisited and improved under many aspects with respect to SmartFoxServer Pro. We have concentrated our attention on the Java Extension development and have dropped support for scripting languages. The main reasons are:
- Performance: extensions written in Java perform orders of magnitude better than any scripting language such as Javascript or Python. With a new server architecture oriented to enterprise-level applications the use of dynamic languages would soon become a bottleneck. We still think that scriping is a great way for quickly prototyping ideas but we don't recommend it for production. You will still be able to plugin dynamic language engines such as Rhino and Jython for this purpose.
- Concurrency: Java allows to take full control over concurrency, which is a critical aspect of server-side code. In particular the latest Java 5/6 concurrent collections and utilities provide the developers with great concurrency tools which would not be fully accessible from dynamic languages.
- Integration: integration with other libraries is more natural if done directly in Java and avoids unexpected consequences that may arise later in the development.
The Java Extensions framework has been vastly improved. The server-side API was written to comply with the latest Java 6 standards, taking advantage of all the latest features such as concurrent collections, generics, enums, annotations, etc. The API also conforms with Java best practices and provide an unprecedented level of customization.
The development cycle has been simplified in order to allow one-click deployment from your IDE and complicated operations such as
classpath modifications or class loading matters are completely managed behind the scenes.
» One Extension to rule everything
The first noteworthy change in the Extensions architecture is that developers can plug a single Extension to their Room or Zone. This is in contrast with SmartFoxServer Pro, where it was possible to attach multiple Extensions. The rationale behind this is that the new Extensions will allow you to do more with less.
- Since a Zone represents an isolated application running in the SmartFoxServer, we find that using a one-extension-per-application model is the most logical way to provide extendibilty.
- An Extension has to be seen as a Java program containing all the logic of your server application. Using simple object OOP practices you can separate the concerns of your code just as you would if you were writing a stand-alone application. The new Extensions API will also assist you in this accomplishment with a new set of tools.
- Using a single Extension allows to keep state in a central place and share it with all the classes in your code.
- Using a single Extension eliminates the need of awkward interoperability tools that just complicate the flow of your code and add class-loading and concurrency issues.
» Extension overview
Java Extensions are deployed in a single .jar file to a folder which represents the name of the Extension. The image below shows the main extensions/ folder which is the root under
which all Extensions are published. Our testExtension folder contains one jar file with all our code.

There are a few more things to notice.
- You can deploy multiple jar files under the same Extension folder, for example dependencies or other libraries. They will all be loaded under the same Class Loader.
- There is a special folder called __lib__ where you can put dependencies as well. This way you can choose which libraries are shared across multiple Extensions and which are local to a specific Extension.
- Libraries deployed under your Extension folder will be loaded in the Extension Class Loader. This means that you can change that specific library without affecting other Extensions.
- Libraries shared under the __lib__ folder are loaded in the parent Class Loader. If you change any of these dependencies it will affect all Extensions that use them.
» Custom configuration
Each Extension can auto-load a .properties file containing custom settings that are immediately available in your code. By default the Extension API will attempt to load a file called config.properties, but you will be able to specify any other file name. This is particularly useful when attaching the same Extension to multiple Zones or Rooms but you need to pass different settings to each one.
This is an example of how an Extension is configured in the AdminTool (see the Zone Extension tab or Room Extension tab in the Zone Configurator module documentation for additional informations and important notes):

Where:
- Name refers the Extension name, represented by the folder name;
- Type indicates the type of Extension in use (Java is recommended);
- Main class is the fully qualified name of the main Extension class (or the Python script file name);
- Use naming convention is not an Extension setting: it just activates/deactivates a filter on the Main class field to show only those classes whose name ends with the word "Extension";
- Properties file is an optional name of .properties file deployed in the Extension folder containing custom Extension settings (default is config.properties);
- Reload mode indicates if the Extension is monitored and auto-reloaded (AUTO), reloaded manually (MANUAL) or not reloadable (NONE).
» Classpath management
The good news is that you don't have to touch the classpath as in SmartFoxServer Pro. SmartFoxServer 2X scans the dependency and Extension folders and loads all the jar files for you.
» Extension reloading
SmartFoxServer 2X provides Extensions hot-redeploy which can be very useful during the development phases. When this feature is
turned on (see the Custom configuration paragraph above), the server will monitor your Extension folders and reload your code when a jar file is modified.
All you need to do is configure your Java IDE to build or copy the jar file directly under the Extension folder in your SmartFoxServer path,
and you will have a one-click deploy system.
» Required dependencies
In order to start creating your own Extensions you will need to add a few libraries to the project in your favorite IDE:
- lib/sfs2x.jar
- lib/sfs2x-core.jar
- lib/log4j-1.2.15.jar
- lib/slf4j-api-1.5.10.jar
- lib/slf4j-log4j12.jar
» Server-side javadoc
You can consult the server-side API javadoc. The main entry point of the API is the com.smartfoxserver.v2.api.SFSApi class. Please notice that the usage of undocumented methods and properties may cause the system malfunctioning.
» A peek at the Extension API
There are two main classes that provide the base for any Extension you will create.
- BaseSFSExtension. Provides the basic four methods already known in SmartFoxServer Pro Extensions development: init, destroy, handleClientRequest, handleServerEvent. We have included this class mainly for compatibility with the previous Extension approach, but we think you will find the next one more convenient.
- SFSExtension. This is the recommended new base class that you should inherit in your Extension main class. SFSExtension provides built-in services for proper separation of request and event handlers, automatic listener release upon destruction of the Extension and more.
» The simplest Extension possible
Let's take a look at the most simple Extension class we can possibly write:
public class MyFirstExtension extends SFSExtension
{
@Override
public void init()
{
trace("Hello, this is my first SFS2X Extension!");
}
}
This is the bare minimum to create a fully functional Extension: one method, init.
Of course you might be already thinking that you will not be able to accomplish much with a single method,
so let's add a request handler. We want the user to be able to send us two numbers and we will add them and send the sum back.
A full video tutorial showing how to create this simple Extension is also available in our YouTube channel.
The recommended practice when developing an Extension is that each request or event handler is a separate class in order to
clearly separate each piece of logic in your code. There are two interfaces we provide to create a Request Handler or a Server Event Handler.
Before we create our addition request handler we have to define which parameters we expect from the client:
We will expect two integers called n1 and n2 and we will send back an integer that is the sum of the two.
Now we can start coding the handler:
public class AddReqHandler extends BaseClientRequestHandler
{
@Override
public void handleClientRequest(User sender, ISFSObject params)
{
// Get the client parameters
int n1 = params.getInt("n1");
int n2 = params.getInt("n2");
// Create a response object
ISFSObject resObj = SFSObject.newInstance();
resObj.putInt("res", n1 + n2);
// Send it back
send("add", resObj, sender);
}
}
The handleClientRequest method receives the following two parameters.
- sender: the User object representing the client which sent the request.
- params: an object with all the parameters sent by the user. The SFSObject is the meat and potato of all objects exchange between client and server. Both the server and client API provide the same ISFSObject interface to ensure coding consistency (read this document for more informations).
The code in the above example should be self-explanatory. We first obtain the two expected integers, prepare a new SFSObject for the response and add the result n1 + n2. Finally we invoke the send method on the parent Extension to send the response back to the user. The following parameters are passed to the method.
- "add": the unique command name used for this request (it is recommended to use the same name within the same request/response pair);
- resObj: the SFSObject containing the data sent to the client;
- sender: the User object representing the client which sent the request is used here, because we want to send the response back to the requester.
In order to "connect" this handler with the main Extension all we need to do is adding one line in the init method of the Extension:
@Override
public void init()
{
trace("Hello, this is my first SFS2X Extension!");
// Add a new Request Handler
addRequestHandler("add", AddReqHandler.class)
}
The addRequestHandler method allows to register an handler for a specific request id. Similarly it is possible to add any numbers of server event handlers using the addEventHandler method. This is an example of how you can listen to the USER_LOGIN server event:
public class LoginEventHandler extends BaseServerEventHandler
{
@Override
public void handleServerEvent(ISFSEvent event) throws SFSException
{
String name = (String) event.getParameter(SFSEventParam.LOGIN_NAME);
if (name.equals("Gonzo") || name.equals("Kermit"))
throw new SFSLoginException("Gonzo and Kermit are not allowed in this Zone!");
}
}
Just like before, we have now to go back to the main Extension class to modify the init method:
@Override
public void init()
{
trace("Hello, this is my first SFS2X Extension!");
// Add a new Request Handler
addRequestHandler("add", AddReqHandler.class)
// Add a new SFSEvent Handler
addEventHandler(SFSEventType.USER_LOGIN, LoginEventHandler.class);
}
Now that we have created our first basic Extension, there are few things that we can note:
- as a difference with the SFS Pro approach, we don't clutter the main Extension code with infinitely long if or switch blocks to dispatch the request or event to the proper handler;
- in SFS2X Extension classes we subscribe to events as opposed to the old system in which all events where always fired to the Extensions even if they didn't need them;
- even it the destroy method seems to be missing, actually it is not: it exists in the parent SFSExtension class and it is not mandatory to override it; if you don't, the default behavior is that all RequestHandlers and EventHandlers are released when the method is called.
If you need to customize the destroy method behaviour you can simply override it. Of course do not forget to call super.destroy to make sure that events/request handlers are auto-unregistered.
@Override
public void destroy()
{
super.destroy()
/*
* More code here...
*/
}
In conclusion it is also worth mentioning some useful methods that are inherited by any command or server handlers:
- getParentExtensions: returns a reference to the main class of your extension;
- getApi: return a reference to the main server-side API object;
- send: sends an Extension message/response to the client(s);
- trace: useful logging method with various signatures (see javadoc for more informations).
» Advanced Extension features
Now that we have covered the basics of the new Extesion 2.0 architecture we can delve into the more advanced features that allow more sophisticated control over your code.
» Instantiation annotations
We provide several useful annotations that can be used to specify how your handler classes should be instantiated. By default when you declare a request/event handler class this will be instantiated as new on every call. You can change this behavior using the @Instantiation annotation on your classes:
- @Instantiation(NEW_INSTANCE): creates a new instance on every call
- @Instantiation(SINGLE_INSTANCE): uses the same instance for all calls
» Multi handlers and the request dot-syntax
In order to properly organize request names in complex application we have established a convention similar to Java package naming that uses a "dot syntax". Suppose your Extension can handle a number of games and other operations such as user registration and profile editing. You can organize all these requests like this:
register.submitFormThe Extension API provides a @MultiHandler annotation that can be added to your handler class definition. This will register the class for all requests starting with a certain prefix. Let's implement a multi-handler for the register set of requests:
register.passwordLost
register.changeEmail
register. ...
register. ...
profile.changeAvatarType
profile.changeNick
profile.getAvatar
profile. ...
profile. ...
checkers.sendMove
checkers.getMyScore
checkers.leaveGame
checkers. ...
checkers. ...
@MultiHandler
public class RegisterMultiHandler extends BaseClientRequestHandler
{
@Override
public void handleClientRequest(User sender, ISFSObject params)
{
// Obtain the request id
String requestId = params.getUtfString(SFSExtension.MULTIHANDLER_REQUEST_ID);
}
}
In the multi-handler, the specific request id among the "register" ones of the example above is obtained from the parameters object passed to the handler. An if or switch statement can be used then to execute the proper code depending on the request id.
Now we register the class in the init method of the Extension as we learnt before.
@Override
public void init()
{
trace("Hello, this is my first multi handler test!");
// Add a new Request Handler
addRequestHandler("register", RegisterMultiHandler.class)
}
The only real difference in this example is that the handler class is marked as @MultiHandler. When this is done, the Extension dispatcher invokes the handler on any request id starting with the "register" prefix. In other words it will handle register.*.
NOTE
You can also mix the @Instantiation annotation with the @MultiHandler one.
In conclusion it is also worth to notice that you are not limited to a single "dot" in the request name. You could have multiple nested levels such as: games.spacewars.fireBullet or user.profile.avatar.getHairColor, etc. The only recommendation we have is to keep these request names reasonably short since they will be transmitted with the request/response objects.
» Extension Filters
The last advanced feature of this tour is Extension Filters. If you are familiar with the Java Servlet API this will probably ring a bell. Extension Filters in SmartFoxServer are inspired by servlet filters and they serve a similar purpose: they are executed in a chain and they can be used to log, filter, or handle specific requests or events before they get to the Extension itself.
The advantage of pluggable Filters is that they don't get in the way of your Extension code, their execution order can be altered and they can even stop the execution flow if necessary. An example of this could be a custom ban filter where user credentials are checked against a black list before the request is passed to your Login Handler.
This is an example of a simple Extension filter:
public class CustomFilter extends SFSExtensionFilter
{
@Override
public void init(SFSExtension ext)
{
super.init(ext);
trace("Filter inited!");
}
@Override
public void destroy()
{
trace("Filter destroyed!");
}
@Override
public FilterAction handleClientRequest(String cmd, User sender, ISFSObject params)
{
// If something goes wrong you can stop the execution chain here!
if (cmd.equals("BadRequest"))
return FilterAction.HALT;
else
return FilterAction.CONTINUE;
}
@Override
public FilterAction handleServerEvent(ISFSEvent event)
{
return FilterAction.CONTINUE;
}
}
Filters can be easily added to any Extension at configuration time or dynamically, at runtime:
@Override
public void init()
{
/*
* This is your Extension main class init()
*/
// Add filters
addFilter("customLoginFilter", new CustomLoginFilter());
addFilter("pubMessageFilter", new PubMessageFilter());
addFilter("privMessageFilter", new PrivMessageFilter());
}
When a new request or event is sent to your Extension it will first traverse the Filters Chain in the order in which Filters were added. In the example above it will be: customLoginFilter » pubMessageFilter » privMessageFilter » Extension.



