title | taxonomy | |||
---|---|---|---|---|
Configuration |
|
We fancy the fact that we made appserver highly configurable. We've provided a centralized configuration file located at /opt/appserver/etc/appserver/appserver.xml
.
This file contains the complete architecture as an XML structure.
To change used components, introduce new services or scale the system by adding additional servers, you can do so with some lines of XML.
In this example, we use a shortened piece of the appserver.xml
file to understand how the architecture is driven by configuration.
<?xml version="1.0" encoding="UTF-8"?>
<appserver xmlns="http://www.appserver.io/appserver">
<!-- define user, group and default umask applied when creating directories and files -->
<params>
<param name="user" type="string">_www</param>
<param name="group" type="string">staff</param>
<param name="umask" type="string">0002</param>
</params>
<containers>
<!-- by default we combine all servers in one container -->
<container name="combined-appserver" type="AppserverIo\Core\GenericContainer">
<description>
<![CDATA[
This is an example of a webserver container
that handles http requests in common way
]]>
</description>
<deployment type="AppserverIo\Appserver\Core\GenericDeployment" />
<host
name="localhost"
appBase="/webapps"
serverAdmin="info@appserver.io"
serverSoftware="appserver/1.0.0.0 (darwin) PHP/5.5.21" />
<servers>
<!-- this is the default configuration for the HTTP server -->
<server
name="http"
type="\AppserverIo\Server\Servers\MultiThreadedServer"
worker="\AppserverIo\Server\Workers\ThreadWorker"
socket="\AppserverIo\Server\Sockets\StreamSocket"
serverContext="\AppserverIo\Server\Contexts\ServerContext"
requestContext="\AppserverIo\Server\Contexts\RequestContext"
loggerName="System">
<!-- define the parameters to configure the server instance -->
<params>
<param name="admin" type="string">info@appserver.io</param>
<param name="software" type="string">
appserver/1.0.0.0 (darwin) PHP/5.5.21
</param>
<param name="transport" type="string">tcp</param>
<param name="address" type="string">127.0.0.1</param>
<param name="port" type="integer">9080</param>
<param name="workerNumber" type="integer">64</param>
<param name="workerAcceptMin" type="integer">3</param>
<param name="workerAcceptMax" type="integer">8</param>
<param name="documentRoot" type="string">webapps</param>
<param name="directoryIndex" type="string">
index.do index.php index.html index.htm
</param>
<param name="keepAliveMax" type="integer">64</param>
<param name="keepAliveTimeout" type="integer">5</param>
<param name="errorsPageTemplatePath" type="string">
var/www/errors/error.phtml
</param>
</params>
<!-- define the environment variables -->
<environmentVariables>
<environmentVariable
condition="" definition="LOGGER_ACCESS=Access" />
</environmentVariables>
<!-- define the connection handler(s) -->
<connectionHandlers>
<connectionHandler
type="\AppserverIo\WebServer\ConnectionHandlers\HttpConnectionHandler" />
</connectionHandlers>
<!-- define authentication basic/digest -->
<authentications>
<authentication uri="^\/admin.*">
<params>
<param name="type" type="string">
\AppserverIo\WebServer\Authentication\BasicAuthentication
</param>
<param name="realm" type="string">
appserver.io Basic Authentication System
</param>
<param name="hash" type="string">
YXBwc2VydmVyOmFwcHNlcnZlci5pMA==
</param>
</params>
</authentication>
</authentications>
<!-- allow access to everything -->
<accesses>
<access type="allow">
<params>
<param name="X_REQUEST_URI" type="string">.*</param>
</params>
</access>
</accesses>
<!-- define a virtual host -->
<virtualHosts>
<virtualHost name="example.local">
<params>
<param name="admin" type="string">
admin@appserver.io
</param>
<param name="documentRoot" type="string">
webapps/example
</param>
</params>
</virtualHost>
</virtualHosts>
<!-- the webserver modules we want to load -->
<modules>
<!-- REQUEST_POST hook -->
<module
type="\AppserverIo\WebServer\Modules\VirtualHostModule"/>
<module
type="\AppserverIo\WebServer\Modules\AuthenticationModule"/>
<module
type="\AppserverIo\WebServer\Modules\EnvironmentVariableModule" />
<module
type="\AppserverIo\WebServer\Modules\RewriteModule"/>
<module
type="\AppserverIo\WebServer\Modules\DirectoryModule"/>
<module
type="\AppserverIo\WebServer\Modules\AccessModule"/>
<module
type="\AppserverIo\WebServer\Modules\CoreModule"/>
<module
type="\AppserverIo\WebServer\Modules\PhpModule"/>
<module
type="\AppserverIo\WebServer\Modules\FastCgiModule"/>
<module
type="\AppserverIo\Appserver\ServletEngine\ServletEngine" />
<!-- RESPONSE_PRE hook -->
<module
type="\AppserverIo\WebServer\Modules\DeflateModule"/>
<!-- RESPONSE_POST hook -->
<module
type="\AppserverIo\Appserver\Core\Modules\ProfileModule"/>
</modules>
<!-- bound the file extensions to the responsible module -->
<fileHandlers>
<fileHandler name="servlet" extension=".do" />
<fileHandler name="fastcgi" extension=".php">
<params>
<param name="host" type="string">127.0.0.1</param>
<param name="port" type="integer">9010</param>
</params>
</fileHandler>
</fileHandlers>
</server>
<!-- Here, additional servers might be added -->
</servers>
</container>
</containers>
</appserver>
In the above example, you can see three important components of the appserver architecture in
use. The container, server and some
modules. We are basically building up a container, which holds a server that uses different modules
to process incoming HTTP (have a look at the connectionHandler
) requests.
When looking at the configuration file of a current installation, it will become visible that certain structures are handled differently on a live system. The most obvious is the usage of the separation of different aspects of the configuration.
The appserver.xml
configuration supports the XInclude mechanism to allow for re-usability.
The following example (which is actually used) shows how the virtual host configuration is separated into an extra file.
<!-- include of virtual host configurations -->
<xi:include href="conf.d/virtual-hosts.xml"/>
This makes virtual hosts re-usable within several servers with just one line within the XML configuration.
A container is created by using the container
element within the containers
collection
of the appserver
document element. Two things make this element into a specific container
being built up by the system on startup:
-
The
type
attribute states a class extending ourAbstractContainerThread
which makes a container into a certain kind of container. -
The
deployment
element states a class containing preparations for starting up the container. It can be considered a hook which will be invoked before the container will be available.
That is basically everything to create a new container. To make use of it, it has to contain at least one server within its servers
collection.
The servers contained by our container can also be loosely drafted by the XML configuration and will be instantiated on container boot-up. To enable a server you have to mention three basic attributes of the element:
- The
type
specifies a class implementing theServerInterface
which implements the basic behavior of the server on receiving a connection and how it will handle it. - The
socket
attribute specifies the type of socket the server should open. E.g. a stream or asynchronous socket - The
serverContext
specifies the server's source of configuration and container for runtime information e.g. ServerVariables likeDOCUMENT_ROOT
So we have the specific server, which will open a certain port and operate in a defined context. But, to make the server handle a certain type of requests, it needs to know which protocol to speak.
This can be done using the connectionHandler
element. Certain server wrappers can handle certain
protocols. Therefore, we can use the protocols, which a server wrapper, e.g. WebServer supports in
form of connection handlers. WebServer
offers an HttpConnectionHandler
class. By using it, the server is able to understand the HTTP
protocol.
The server configuration makes heavy use of the param
element, which is used to apply some of the most important configuration values to a server instance.
An example of the params a server can take can be found in the example below.
<params>
<param name="admin" type="string">info@appserver.io</param>
<param name="software" type="string">
appserver/1.0.0 (darwin) PHP/5.5.21
</param>
<param name="transport" type="string">tcp</param>
<param name="address" type="string">127.0.0.1</param>
<param name="port" type="integer">9080</param>
<param name="workerNumber" type="integer">64</param>
<param name="workerAcceptMin" type="integer">3</param>
<param name="workerAcceptMax" type="integer">8</param>
<!-- ... -->
</params>
Some of these params do speak for themselves, but others don't. You can find a complete list of their meaning below:
Param name | Type | Description |
---|---|---|
admin |
string | The email address of the administrator who is responsible for the server. |
software |
string | The software signature, as shown in the response header for example. |
transport |
string | The transport layer. In ssl mode ssl will be used instead of plain tcp . |
address |
string | The address the server-socket should bind and listen to. If you want to allow only connection on local loopback define 127.0.0.1 as in the example above shown. This will be good enough for local development and testing purpose. If you want to allow connections to your external ethernet interfaces, just define 0.0.0.0 or if you want to allow connection only on a specific interface, just define the ip of your interface 192.168.1.100 . |
port |
integer | The port for the server-socket to accept connections to. This can be any common port number. Make sure there is no other server installed blocking the default ports. |
workerNumber |
integer | Defines the number of worker-queues to be started waiting for requests to process. |
workerAcceptMin |
integer | Describes the minimum number of requests for the worker to be accepted to randomize its lifetime. |
workerAcceptMax |
integer | Describes the maximum number of requests for the worker to be accepted tor randomize its lifetime. |
All params listed above are common to servers using the HttpConnectionHandler
.
The param composition may vary depending on the server implementation.
Since version 1.1 appserver.io also provides a real CRON implementation that can replace your system's CRON daemon. The jobs can be configured in a separate XML configuration file, located under etc/appserver/conf.d/cron.xml
.
The following example shows the configuration for a simple CRON job that writes the application servers PHP version to the var/log/php_errors.log
file every minute.
<?xml version="1.0" encoding="UTF-8"?>
<cron xmlns="http://www.appserver.io/appserver">
<jobs>
<job name="test-02">
<schedule>0 * * * * *</schedule>
<execute directory="/opt/appserver" script="bin/php">
<args>
<arg type="string">-v</arg>
</args>
</execute>
</job>
</jobs>
</cron>
The configuration of a job will need the name
attribute.
- The
name
attribute has to contain a unique job name, as well as<schedule/>
and<execute/>
subnodes
The <schedule/>
node's value must be a valid CRON expression, whereas the <execute/>
node has the two attributes directory
and script
.
directory
has to contain the working directory the job will be executed inscript
the name of the script or binary that has to be executed
Both values can contain an absolute or a relativ path. If the path is relative, the CRON job assumes that the root is the application server's base directory. Optionally, the <execute/>
node can have a subnode <args/>
that can have numerous <arg/>
nodes containing the parameters that has to be passed to the script, when it'll be executed.
In addition to the Container and Server configurations, it is also possible to configure the applications. Each application
can have its own autoloaders and managers. By default, each application found in the application
server's webapp directory /opt/appserver/webapps
will be initialized with the defaults, defined
in /opt/appserver/etc/appserver/conf.d/context.xml
<?xml version="1.0" encoding="UTF-8"?>
<context
type="AppserverIo\Appserver\Application\Application">
<classLoaders>
<!-- necessary to load files from the vendor directory of your application -->
<classLoader
name="ComposerClassLoader"
interface="ClassLoaderInterface"
type="AppserverIo\Appserver\Core\ComposerClassLoader"
factory="AppserverIo\Appserver\Core\ComposerClassLoaderFactory">
<directories>
<directory>/vendor</directory>
</directories>
</classLoader>
<!-- necessary to load files from WEB-INF/classes and META-INF/classes, also -->
<!-- provides the functionality for Design-by-Contract and AOP -->
<classLoader
name="DgClassLoader"
interface="ClassLoaderInterface"
type="AppserverIo\Appserver\Core\DgClassLoader"
factory="AppserverIo\Appserver\Core\DgClassLoaderFactory">
<params>
<param name="environment" type="string">production</param>
<param name="enforcementLevel" type="integer">7</param>
<param name="typeSafety" type="boolean">1</param>
<param name="processing" type="string">logging</param>
</params>
<directories>
<directory enforced="true">/common/classes</directory>
<directory enforced="true">/WEB-INF/classes</directory>
<directory enforced="true">/META-INF/classes</directory>
</directories>
</classLoader>
</classLoaders>
<managers>
<!-- provides services necessary for DI -->
<manager
name="Provider"
beanInterface="ProviderInterface"
type="AppserverIo\Appserver\DependencyInjectionContainer\Provider"
factory="AppserverIo\Appserver\DependencyInjectionContainer\ProviderFactory"/>
<!-- provides the services necessary to handle Session- and MessageBeans -->
<manager
name="BeanManager"
beanInterface="BeanContextInterface"
type="AppserverIo\Appserver\PersistenceContainer\BeanManager"
factory="AppserverIo\Appserver\PersistenceContainer\BeanManagerFactory">
<!-- params>
<param name="lifetime" type="integer">1440</param>
<param name="garbageCollectionProbability" type="float">0.1</param>
</params -->
</manager>
<!-- provides the functionality to define and run a Queue -->
<manager
name="QueueManager"
beanInterface="QueueContextInterface"
type="AppserverIo\Appserver\MessageQueue\QueueManager"
factory="AppserverIo\Appserver\MessageQueue\QueueManagerFactory"/>
<!-- provides the functionality to define Servlets handling HTTP request -->
<manager
name="ServletManager"
beanInterface="ServletContextInterface"
type="AppserverIo\Appserver\ServletEngine\ServletManager"
factory="AppserverIo\Appserver\ServletEngine\ServletManagerFactory">
<directories>
<directory enforced="true">/WEB-INF/classes</directory>
<directory enforced="true">/vendor/appserver-io/routlt/src</directory>
</directories>
</manager>
<!-- provides functionality to handle HTTP sessions -->
<manager
name="StandardSessionManager"
beanInterface="SessionManagerInterface"
type="AppserverIo\Appserver\ServletEngine\StandardSessionManager"
factory="AppserverIo\Appserver\ServletEngine\StandardSessionManagerFactory"/>
<!-- provides functionality to handle Timers -->
<manager
name="TimerServiceRegistry"
beanInterface="TimerServiceContextInterface"
type="AppserverIo\Appserver\PersistenceContainer\TimerServiceRegistry"
factory="AppserverIo\Appserver\PersistenceContainer\TimerServiceRegistryFactory"/>
<!-- provides functionality to handle HTTP basic/digest authentication -->
<manager
name="StandardAuthenticationManager"
beanInterface="AuthenticationManagerInterface"
type="AppserverIo\Appserver\ServletEngine\Authentication\StandardAuthenticationManager"
factory="AppserverIo\Appserver\ServletEngine\Authentication\StandardAuthenticationManagerFactory"/>
<!-- provides functionality to preload Advices found in WEB-INF/classes or META-INF/classes -->
<manager
name="AspectManager"
beanInterface="AspectManagerInterface"
type="AppserverIo\Appserver\AspectContainer\AspectManager"
factory="AppserverIo\Appserver\AspectContainer\AspectManagerFactory"/>
</managers>
</context>
If your application does not use any of the defined class loaders or managers, or you want to implement
your own managers, you can define them in a context.xml
file, that you have to deliver with your
application. Your own, customized file has to be stored in META-INF/context.xml
. When the application
server starts, this file will be parsed and your application will be initialized with the defined class loaders
and managers.
Please be aware, that the default class loaders and managers provide most of the functionality described above. If you remove them from the
context.xml
you have to anticipate unexpected behavior.
As you might need an environment switch for your application, to handle things differently e.g. turn of authentication in development mode or use different database connections, you can specify as many environments as you like by following this naming convention:
META-INF/context.production.xml
or META-INF/context.development.xml
would cause your application to have 2 different environments
production
and development
which you can use to switch configurations as you wish.
In your application code you can check which environment is currently active by injecting the application and checking its environment:
/**
* @var \AppserverIo\Appserver\Application\Application
* @Resource(name="ApplicationInterface")
*/
protected $application;
public function doSomething()
{
if ($this->application->getEnvironmentName() === 'production') {
// do something different
}
}
To specify the variable, set it in a build.properties
file which resides in your application's root directory:
appserver.webapp.environment = development
This will result in a preference for all XML configuration containing the .development.xml
suffix over their non-suffixed counterpart.
If no suffixed file exists, the default file will be loaded instead.
The application itself allows many configuration options. To make things more comfortable, we provide a default configuration, that should fit most of the common requirements. These default options can be overwritten, but NOT removed, in the application specific configuration files that resides in the META-INF
and WEB-INF
directories. These directories are intended to be the default directories for the application specific configuration and it's classes.
To make the application configuration as generic as possible, it is possible to use variables, that will be populated with the real values at runtime. This will help, to write an application specific configuration, that'll work system independent in nearly every appserver.io installation.
The following variables are available and can be used in most of the application specific configuration files
Variable name | Type | Description |
---|---|---|
base.dir |
string | The installation directory, /opt/appserver on Linux and Mac OS X. |
var.log.dir |
string | The directory containing the log files, defaults to /opt/appserver/var/log . |
etc.dir |
string | The configuration base directory, defaults to /opt/appserver/etc . |
etc.appserver.dir |
string | The directory that contains the appserver.io main configuration file, defaults to /opt/appserver/etc . |
etc.appserver.confd.dir |
string | Directory that contains additional appserver.io specific configuration files, defaults to /opt/appserver/etc/appserver/conf.d . |
tmp.dir |
string | The temporary directory used by PHP and is configured in php.ini and php-fpm-fcgi.ini as upload_tmp_dir . |
webapps.dir |
string | Contains the absolute path to the container specific directory with the deployed web applications, defaults to /opt/appserver/webapps . |
host.appBase.dir |
string | Contains the path, relative to base.dir with the container specific directory with the deployed web applications, defaults to webapps . |
host.tmpBase.dir |
string | Contains the path, relative to base.dir with the container specific directory temporary directory, defaults to webapps . |
host.deployBase.dir |
string | Contains the path, relative to base.dir with the container specific deploy with the PHAR archives to be deployed, defaults to webapps . |
container.name |
string | The name of the container the application has been deployed in, defaults to combined-appserver . |
webapp.name |
string | The name of the deployed web application, defaults to the application directory, e. g. example for the example application |
As it some of the configuration are, by definition, system independent the variables can be used in the following configuration files
Configuration filename | Type | Description |
---|---|---|
META-INF/cron.xml |
string | The application specific CRON configuration. |
META-INF/*-ds.xml |
string | One or more datasources that will be part of an application. |
META-INF/context.xml |
string | The application's main configuration file. |
META-INF/provision.xml |
string | Provisioning configuration for the application. |
META-INF/containers.xml |
string | Override, extend or replace appserver.io's container and/or server configuration. |
META-INF/persistence.xml |
string | Configuration of the application's Doctrine entity manager(s). |
META-INF/message-queues.xml |
string | The application's message queue configuration. |
You can't use it in
Configuration filename | Type | Description |
---|---|---|
META-INF/epb.xml |
string | The application's session and message bean configuration. |
META-INF/pointcuts.xml |
string | The AOP configuration of the application's session and message beans. |
WEB-INF/web.xml |
string | The main web application configuration. |
WEB-INF/pointcuts.xml |
string | The AOP configuration of the web appliation. |
as these configuration files, are by definitiion, system independent and there is no need to use variables.
The variables can be referenced with the default properties file notation ${VARIABLE-NAME}
. A appserver.io independend virtual host configuration META-INF/containers.xml
for your web application, that makes heavy usage of variables, would look like the following example.
<?xml version="1.0" encoding="UTF-8"?>
<containers xmlns="http://www.appserver.io/appserver">
<container name="${container.name}">
<servers>
<server name="http*">
<virtualHosts>
<virtualHost name="${webapp.name}.dev www.${webapp.name}.dev">
<params>
<param name="admin" type="string">info@appserver.io</param>
<param name="documentRoot" type="string">${host.appBase.dir}/${webapp.name}</param>
</params>
</virtualHost>
</virtualHosts>
</server>
</servers>
</container>
</containers>
This example also uses the wildcard pattern http*
for the server name, that will be described in chapter Create/Override/Extends Server Configuration later on.
Additional to the hard coded configuration variables, that can be used in the system configuration, it is possible to declare additions system properties that can also be used in the same way as variables in the system configuration.
For example, if an environment variable MY_DOMAIN
, passed by Docker to the OS, should be used as domain for a virtual host, it can be declared as system property with
<systemProperties>
<systemProperty name="my.domain" type="string" env="true">MY_DOMAIN</systemProperty>
</systemProperties>
Then it is possible to use the system property my.domain
in the etc/appserver/conf.d/virtual-hosts.xml
like
<virtualHosts xmlns="http://www.appserver.io/appserver">
<virtualHost name="${my.domain}">
<params>
<param name="admin" type="string">info@appserver.io</param>
<param name="documentRoot" type="string">webapps</param>
</params>
</virtualHosts>
</virtualHosts>
This allows a very generic system configuration, especially when using appserver.io with Docker or similar technologies.
Each application can have its own classloaders, loggers and managers. As mentioned before, each application, found a the container's webapp directory will be initialized with the defaults, defined in etc/appserver/conf.d/context.xml
. This files has the following content
<?xml version="1.0" encoding="UTF-8"?>
<context
name="globalBaseContext"
factory="AppserverIo\Appserver\Application\ApplicationFactory"
type="AppserverIo\Appserver\Application\Application"
xmlns="http://www.appserver.io/appserver">
<classLoaders>
<classLoader
name="ComposerClassLoader"
interface="ClassLoaderInterface"
type="AppserverIo\Appserver\Core\ComposerClassLoader"
factory="AppserverIo\Appserver\Core\ComposerClassLoaderFactory">
<directories>
<directory>/vendor</directory>
</directories>
</classLoader>
<classLoader
name="DgClassLoader"
interface="ClassLoaderInterface"
type="AppserverIo\Appserver\Core\DgClassLoader"
factory="AppserverIo\Appserver\Core\DgClassLoaderFactory">
<params>
<param name="environment" type="string">production</param>
<param name="enforcementLevel" type="integer">7</param>
<param name="typeSafety" type="boolean">1</param>
<param name="processing" type="string">logging</param>
</params>
<directories>
<directory enforced="true">/common/classes</directory>
<directory enforced="true">/WEB-INF/classes</directory>
<directory enforced="true">/META-INF/classes</directory>
</directories>
</classLoader>
</classLoaders>
<loggers>
<logger channelName="system" name="System" type="\AppserverIo\Logger\Logger">
<handlers>
<handler type="\AppserverIo\Logger\Handlers\CustomFileHandler">
<formatter type="\AppserverIo\Logger\Formatters\StandardFormatter"/>
<params>
<param name="logFile" type="string">var/log/${webapp.name}-errors.log</param>
<param name="logLevel" type="string">info</param>
</params>
</handler>
</handlers>
</logger>
<logger channelName="access" name="Access" type="\AppserverIo\Logger\Logger">
<handlers>
<handler type="\AppserverIo\Logger\Handlers\CustomFileHandler">
<formatter type="\AppserverIo\Logger\Formatters\StandardFormatter">
<params>
<param name="format" type="string">%4$s</param>
</params>
</formatter>
<params>
<param name="logFile" type="string">var/log/${webapp.name}-access.log</param>
<param name="logLevel" type="string">info</param>
</params>
</handler>
</handlers>
</logger>
</loggers>
<managers>
<manager name="ObjectManagerInterface" type="AppserverIo\Appserver\DependencyInjectionContainer\ObjectManager" factory="AppserverIo\Appserver\DependencyInjectionContainer\ObjectManagerFactory">
<descriptors>
<descriptor>AppserverIo\Description\ServletDescriptor</descriptor>
<descriptor>AppserverIo\Description\MessageDrivenBeanDescriptor</descriptor>
<descriptor>AppserverIo\Description\StatefulSessionBeanDescriptor</descriptor>
<descriptor>AppserverIo\Description\SingletonSessionBeanDescriptor</descriptor>
<descriptor>AppserverIo\Description\StatelessSessionBeanDescriptor</descriptor>
</descriptors>
</manager>
<manager name="ProviderInterface" type="AppserverIo\Appserver\DependencyInjectionContainer\Provider" factory="AppserverIo\Appserver\DependencyInjectionContainer\ProviderFactory"/>
<manager name="PersistenceContextInterface" type="AppserverIo\Appserver\PersistenceContainer\PersistenceManager" factory="AppserverIo\Appserver\PersistenceContainer\PersistenceManagerFactory"/>
<manager name="BeanContextInterface" type="AppserverIo\Appserver\PersistenceContainer\BeanManager" factory="AppserverIo\Appserver\PersistenceContainer\BeanManagerFactory">
<directories>
<directory>/META-INF/classes</directory>
</directories>
</manager>
<manager name="QueueContextInterface" type="AppserverIo\Appserver\MessageQueue\QueueManager" factory="AppserverIo\Appserver\MessageQueue\QueueManagerFactory">
<params>
<param name="maximumJobsToProcess" type="integer">200</param>
</params>
</manager>
<manager name="ServletContextInterface" type="AppserverIo\Appserver\ServletEngine\ServletManager" factory="AppserverIo\Appserver\ServletEngine\ServletManagerFactory">
<directories>
<directory>/WEB-INF/classes</directory>
</directories>
</manager>
<manager name="SessionManagerInterface" type="AppserverIo\Appserver\ServletEngine\StandardSessionManager" factory="AppserverIo\Appserver\ServletEngine\StandardSessionManagerFactory">
<sessionHandlers>
<sessionHandler name="filesystem" type="AppserverIo\Appserver\ServletEngine\Session\FilesystemSessionHandler" factory="AppserverIo\Appserver\ServletEngine\Session\SessionHandlerFactory"/>
</sessionHandlers>
</manager>
<manager name="TimerServiceContextInterface" type="AppserverIo\Appserver\PersistenceContainer\TimerServiceRegistry" factory="AppserverIo\Appserver\PersistenceContainer\TimerServiceRegistryFactory"/>
<manager name="AuthenticationManagerInterface" type="AppserverIo\Appserver\ServletEngine\Security\StandardAuthenticationManager" factory="AppserverIo\Appserver\ServletEngine\Security\StandardAuthenticationManagerFactory">
<authenticators>
<authenticator name="Form" type="AppserverIo\Appserver\ServletEngine\Authenticator\FormAuthenticator" />
<authenticator name="Basic" type="AppserverIo\Appserver\ServletEngine\Authenticator\BasicAuthenticator" />
<authenticator name="Digest" type="AppserverIo\Appserver\ServletEngine\Authenticator\DigestAuthenticator" />
</authenticators>
</manager>
<manager name="AspectManagerInterface" type="AppserverIo\Appserver\AspectContainer\AspectManager" factory="AppserverIo\Appserver\AspectContainer\AspectManagerFactory"/>
</managers>
<provisioners>
<provisioner name="standard" factory="AppserverIo\Appserver\Provisioning\StandardProvisionerFactory" type="AppserverIo\Appserver\Provisioning\StandardProvisioner" />
</provisioners>
</context>
If the application does not make use of any of the defined classloaders, loggers or managers, or addtional managers are necessary, it is possible to define them in an application specific file, that has to be delivered with the application itself. This customized file has to be stored in META-INF/context.xml
. When the application server starts, it will be parsed and the application will be initialized with the defined classloaders, loggers and managers.
Please be aware, that the default classloaders, loggers and managers provides most of the functionality a web applications makes use of. They can NOT simply be removed by commenting them in the application's
META-INF/context.xml
, because the values in the template will be used instead. To remove them completely, they've also be commented in the templateetc/appserver/conf.d/context.xml
, which may, with a high probability, result in an unexpected behavior.
The default application configuration above defines two application sepcific classloaders. The first classloader is responsible to load the classes of the composer libraries, delivered in the application's vendor directory. The second one is responsible for the application specific classes that resides below the common/classes
, META-INF/classes
and WEB-IN/classes
directories. These higher priorized classloader, namely AppserverIo\Appserver\Core\DgClassLoader
provides additional functionality, like generating the class stubs, that are necessary for the AOP and Design-by-Contract functionality. So these classloader MUST NOT be removed or replaced.
By default, an application comes with two registered loggers, an access and a system logger. The access logger can be used to have a separate access log for an application, the system logger to have an application specific log file for debugging purposes. The access logger is not used by default. To activate it, an environment variable has to be set in the application's virtual host configuration, which can be done in the META-INF/containers.xml
, and will be described in the next chapter. The application's system logger can be used wherever an application instance is available or by loading it from the Naming Directory. For example in a servlet, the application's system logger can be accessed by
<?php
namespace AppserverIo\Example\Servlets;
use AppserverIo\Psr\Servlet\Http\HttpServlet;
use AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface;
use AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface;
/**
* This is the famous 'Hello World' as servlet implementation.
*
* @Route(name="helloWorld",
* urlPattern={"/helloWorld.do", "/helloWorld.do*"})
*/
class HelloWorldServlet extends HttpServlet
{
/**
* The application instance that provides the entity manager.
*
* @var \AppserverIo\Psr\Application\ApplicationInterface
* @Resource(name="ApplicationInterface")
*/
protected $application;
/**
* Handles a HTTP GET request.
*
* @param \AppserverIo\Psr\Servlet\Http\HttpServletRequestInterface $servletRequest
* The request instance
* @param \AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface $servletResponse
* The response instance
*
* @return void
* @see \AppserverIo\Psr\Servlet\Http\HttpServlet::doGet()
*/
public function doGet(
HttpServletRequestInterface $servletRequest,
HttpServletResponseInterface $servletResponse)
{
// first log 'Hello World!' to the application's system logger
$this->application->getLogger()->info($message = 'Hello World!');
// then append it to the response body
$servletResponse->appendBodyStream($message);
}
}
A big part of the application server core functionality is capsuled in Manager implementations.
As this is a large topic, we've separated the Managers documentation to a new page, which contains all the necessary information you need to understand which managers are available, how they can be configured and how a new Manager can be implemented.
An application can have it's own provisioning implementation. The provisioning can be configured in a configuration file META-INF/provision.xml
and allows the definition of steps. Each step can define a type, which reflects a class name, that will be instanciated and executed during the application server's startup. Beside the steps, the configuration allows the specification of a datasource, which can be used within a step , e. g. to access the data.
<?xml version="1.0" encoding="UTF-8"?>
<provision xmlns="http://www.appserver.io/appserver">
<datasource name="appserver.io-example-application"/>
<installation>
<steps>
<step type="AppserverIo\Apps\Example\Provisioning\PrepareDatabaseStep" />
</steps>
</installation>
</provision>
The provisioning process itself and each step will be executed in a separate thread, in a sychronous manner. The step AppserverIo\Apps\Example\Provisioning\PrepareDatabaseStep
, which is part of our example application uses a Stateless Session Bean to create an empty database as well as default credentials and products.
Actual, the provisioning process has no mechanism to query whether or not the application state, e. g. if this is the first installation or an update. This functionality can depend for each application and therefore has to be implemented by application vendor itself.
Since version 1.1 you have the possiblity to create a new server as well as override or extend parts of the existing server configuration, assumed you have activated that functionality. This functionality will be activated by default. If not, you can set the param
with the name allowApplicationConfiguration
<?xml version="1.0" encoding="UTF-8"?>
<appserver xmlns="http://www.appserver.io/appserver" xmlns:xi="http://www.w3.org/2001/XInclude">
<params>
<param name="user" type="string">_www</param>
<param name="group" type="string">staff</param>
<param name="umask" type="string">0002</param>
<param name="allowApplicationConfiguration" type="boolean">true</param>
</params>
...
in etc/appserver/appserver.xml
to true
.
If that flag is activated, you can deliver a completely separate container configuration with servers, virtual hosts and all allowed configuration parameters. The configuration file has to be located in the META-INF
directory of your application and named containers.xml
.
For example, if you want to deliver your own virtual host configuration with your application, your configuration file would look like this.
<?xml version="1.0" encoding="UTF-8"?>
<containers xmlns="http://www.appserver.io/appserver">
<container name="${container.name}">
<servers>
<server name="http*">
<virtualHosts>
<virtualHost name="${webapp.name}.dev www.${webapp.name}.dev">
<params>
<param name="admin" type="string">info@appserver.io</param>
<param name="documentRoot" type="string">${host.appBase.dir}/${webapp.name}</param>
</params>
<rewrites>
<rewrite condition="-d{OR}-f{OR}-l" target="" flag="L" />
</rewrites>
<accesses>
<access type="allow">
<params>
<param name="X_REQUEST_URI" type="string">^.*</param>
</params>
</access>
</accesses>
<environmentVariables>
<environmentVariable condition="" definition="LOGGER_ACCESS=${container.name}/${webapp.name}/Access" />
</environmentVariables>
</virtualHost>
</virtualHosts>
</server>
</servers>
</container>
</containers>
The example above also defines an environment variable, that activates an access log for the application. This results in a separate access log file that can be configured in the logger configuration of the application's META-INF/context.xml
file. As the access logger will be looked up by using the Naming Directory, the name specified in the LOGGER_ACCESS
environment variable has to be prefixed with the container and the web application name.
To avoid writing virtual host configurations twice, one for the http
and one for the https
server, also wildcards can be used. Instead of using http
or https
as server name, http*
can be specified. When the configuration file will be parsed on the application server's startup, the PHP fnmatch()
method will be used to resolve the matching servers and apply the virtual host configuration to them.
In order to extend or override an existing configuration, it is necessary either to use the same names of the container or server you wish to extend, variables or wildcards (for container/server names only). If you do not use the same or matching names, you will end up creating a NEW container or server, which is probably not the outcome you expected. To find the container or server names, please refer to the application server's default configuration in
etc/appserver/appserver.xml
.
Beside an application specific container and server configuration, it is also possible to deliver an application specific CRON configuration. The application specific CRON configuration has the same file structure as the global one and has to be located in META-INF/cron.xml
. As well as in the other application specific configuration files, the usage of variables is supported and strongly recommended to make the configuration system independent.
The following example shows a valid Magento 2 CRON configuration
<?xml version="1.0" encoding="UTF-8"?>
<cron xmlns="http://www.appserver.io/appserver">
<jobs>
<job name="${webapp.name}-default-cron">
<schedule>* * * * * *</schedule>
<execute directory="${webapp.dir}" script="${base.dir}/bin/php">
<args>
<arg type="string">bin/magento</arg>
<arg type="string">cron:run</arg>
</args>
</execute>
</job>
<job name="${webapp.name}-setup-cron">
<schedule>* * * * * *</schedule>
<execute directory="${webapp.dir}" script="${base.dir}/bin/php">
<args>
<arg type="string">bin/magento</arg>
<arg type="string">setup:cron:run</arg>
</args>
</execute>
</job>
</jobs>
</cron>
For more information about the CRON configuration options, have a look at the System CRON chapter above.
The web server comes with a package of default modules. The functionality that allows you to configure a virtual host or environment variables, for example, is also provided by two of, probably the most, important modules.
This module can be used according to the \AppserverIo\WebServer\Interfaces\HttpModuleInterface
interface.
It needs an initial call of the init
method and will process any request offered to the process
method.
The module is best used within the webserver
project, as it offers all needed infrastructure.
One of the most important parts of the module is the way it can perform rewrites. All rewrites are based on rewrite rules, which consist of three important parts:
-
condition string : Conditions to be met in order for the rule to take effect. See more down here
-
target string : The target to rewrite the requested URI to. Within this string, you can use backreferences similar to the Apache mod_rewrite module with the difference that you have to use the
$ syntax
(instead of the$/%/%{} syntax
of Apache).Matching rule conditions via regex are also part of available backreferences, as well as server and environment variables.
Simple example : A condition like
(.+)@$X_REQUEST_URI
would produce a back reference$1
with the value/index
for a requested URI/index
. The target string$1/welcome.html
would, therefore, result in a rewrite to/index/welcome.html
-
flag string : Use flags, similar to mod_rewrite, to make rules, which react in a certain way or influence further processing. Learn more about flags below.
The Syntax of conditions is roughly based on the combination of Apache's RewriteCondition and RewriteRule syntax.
To make use of such a combination, you can chain conditions together using the {OR}
symbol for
OR-combined and the {AND}
symbol for AND-combined conditions.
Please be aware that AND takes precedence over OR! Conditions can either be PCRE regex or certain fixed
expressions. So a condition string of ([A-Z]+\.txt){OR}^/([0-9]+){AND}-f
would match only real files
(through -f
), which either begins with numbers or end with capital letters and the extension .txt.
As you might have noticed: Backslashes do not have to be escaped.
You might also be curious about the -f
condition. This is a direct copy of Apaches -f RewriteCondition.
We also support several other expressions of regex based conditions which are:
- <<COMPARE_STRING> : Is the operand lexically preceding
<COMPARE_STRING>
? - ><COMPARE_STRING> : Is the operand lexically following
<COMPARE_STRING>
? - =<COMPARE_STRING> : Is the operand lexically equal to
<COMPARE_STRING>
? - -d : Is the operand a directory?
- -f : Is the operand a real file?
- -s : Is the operand a real file of a size greater than 0?
- -l : Is the operand a symbolic link?
- -x : Is the operand an executable file?
If you are wondering what the operand
might be: it is whatever you want it to be! You can specify
any operand you'd like using the @
symbol. All conditions of a rule will use the next operand to
their right, and if no operand is given, the module will simply use the requested URI. For example:
([A-Z]+\.txt){OR}^/([0-9]+)
Will take the requested URI for both conditions (note the{OR}
symbol)([A-Z]+\.txt){OR}^/([0-9]+)@$DOCUMENT_ROOT
Will test both conditions against the document root([A-Z]+\.txt)@$DOCUMENT_ROOT{OR}^/([0-9]+)
Will only test the first one against the document root and the second against the requested URI
You might have noted the $
symbol before DOCUMENT_ROOT
and remembered it from the backreference
syntax. That is because all Apache common server vars can be explicitly used as backreferences too!
This doesn't work for you? Need the exact opposite? No problem!
All conditions, regex or expression based, can be negated using the !
symbol in front of
them! So !^([0-9]+)
would match all strings which do NOT begin with a number and !-d
would match
all non-directories.
Flags are used to further influence processing. You can specify as many flags per rewrite as you'd like,
but be aware of their impact! Syntax for several flags is simple: just separate them with a ,
symbol.
Flags, which might accept a parameter, can be assigned one by using the =
symbol. Currently supported
flags are:
-
L : As rules are normally processed one after the other, the
L
flag will make the flagged rule the last one processed, if matched. -
R : If this flag is set, we will redirect the client to the URL specified in the
target string
. If this is just a URI, we will redirect to the same host. You might also specify a custom status code between 300 and 399, to indicate the reason for or the kind of the redirect. Default is301
akapermanent
-
M : Stands for map. Using this flag you can specify an external source (have a look at the Injector classes of the WebServer project) of a target map. With
M=<MY_BACKREFERENCE>
you can specify what the map's index has to match. This matching is done only if the rewrite condition matches and will behave as another condition.
This module can be used according to the \AppserverIo\WebServer\Interfaces\HttpModuleInterface
interface. It needs an initial call of the init
method and will process any request offered to
the process
method. The module is best used within the webserver
project, as it offers all the needed infrastructure.
If you need to configure a virtual host, it should look like the
following example, which would enable a Magento installation under http://magento.dev:9080
.
<virtualHosts>
<virtualHost name="magento.dev">
<params>
<param name="admin" type="string">info@appserver.io</param>
<param name="documentRoot" type="string">webapps/magento</param>
</params>
<rewrites>
<rewrite condition="-d{OR}-f{OR}-l" target="" flag="L" />
<rewrite condition="(.*)" target="index.php/$1" flag="L" />
</rewrites>
<accesses>
<access type="allow">
<params>
<param name="X_REQUEST_URI" type="string">
^\/([^\/]+\/)?(media|skin|js|index\.php).*
</param>
</params>
</access>
</accesses>
</virtualHost>
</virtualHosts>
You might be curious about the different ports we use. Per default the appserver will open several ports where its services are available. As we do not want to block (or be blocked by) other services we use ports of a higher range.
As a default we use the following ports:
-
WebContainer
- HTTP Server:
9080
- HTTPS Server:
9443
- HTTP Server:
-
Persistence-MQ-Container
- Persistence-Container:
8585
- Message-Queue:
8587
- Persistence-Container:
You can change this default port mapping by using the server configuration.
Simplicity has always been in our main focus. Therefore we do provide several configuration defaults, which are not even shown in the configuration file, as their default setup works very well out of the box. You might change these values and we do not want to stand in your way. So following are some configurable components, which are already configured implicitly, but can be explicitly set up in the configuration files.
Extractors are used to process any form of archive in an ETL like manner.
The example shown below is used to un-pack webapps, which are provided as phar
archives upon deployment.
<extractors>
<extractor name="phar" type="AppserverIo\Appserver\Core\Extractors\PharExtractor" createBackups="false" restoreBackups="false" />
</extractors>
The initial context is the configurational heart of the running appserver instance and manages instance creation at a low level. If really needed for any custom core functionality, you are able to change its composition here.
<initialContext type="AppserverIo\Appserver\Core\InitialContext">
<description><![CDATA[The initial context configuration.]]></description>
<classLoader name="default" type="AppserverIo\Appserver\Core\SplClassLoader" />
<storage type="AppserverIo\Storage\StackableStorage" />
</initialContext>
Loggers can be used to log specific types of messages in a specific way. This is highly configurable and by default there are three loggers:
- system logger -> appserver-errors.log
- access logger -> appserver-access.log
- error logger -> php_errors.log
Loggers are configured within the appserver
node:
<loggers>
<logger channelName="system" name="System" type="\AppserverIo\Logger\Logger">
<handlers>
<handler type="\AppserverIo\Logger\Handlers\CustomFileHandler">
<formatter type="\AppserverIo\Logger\Formatters\StandardFormatter"/>
<params>
<param name="logFile" type="string">var/log/appserver-errors.log</param>
<param name="logLevel" type="string">info</param>
</params>
</handler>
</handlers>
</logger>
<logger channelName="access" name="Access" type="\AppserverIo\Logger\Logger">
<handlers>
<handler type="\AppserverIo\Logger\Handlers\CustomFileHandler">
<formatter type="\AppserverIo\Logger\Formatters\StandardFormatter">
<params>
<param name="format" type="string">%4$s</param>
</params>
</formatter>
<params>
<param name="logFile" type="string">var/log/appserver-access.log</param>
<param name="logLevel" type="string">info</param>
</params>
</handler>
</handlers>
</logger>
<logger channelName="profile" name="Profile" type="\AppserverIo\Logger\Logger">
<processors>
<processor type="\AppserverIo\Logger\Processors\MemoryProcessor"/>
<processor type="\AppserverIo\Logger\Processors\SysloadProcessor"/>
<processor type="\AppserverIo\Logger\Processors\ThreadContextProcessor"/>
</processors>
<handlers>
<handler type="\AppserverIo\Logger\Handlers\LogstashHandler">
<params>
<param name="host" type="string">127.0.0.1</param>
<param name="port" type="integer">9514</param>
<param name="logLevel" type="string">debug</param>
</params>
</handler>
<handler type="\AppserverIo\Logger\Handlers\CustomFileHandler">
<params>
<param name="logFile" type="string">var/log/appserver-profile.log</param>
<param name="logLevel" type="string">debug</param>
</params>
</handler>
</handlers>
</logger>
</loggers>
Provisioners can be used to automatically setup webapps upon their deployment. You might integrate your own, using provided steps, or completely code new ones. The shown example creates data sources configured within the application.
<provisioners>
<provisioner name="datasource" type="AppserverIo\Appserver\Core\DatasourceProvisioner" />
<provisioner name="standard" type="AppserverIo\Appserver\Core\StandardProvisioner" />
</provisioners>
Scanners are classes reacting to file system changes and can be configured within the appserver
node.
You might want to use this feature to tightly integrate a deployment scanner, like used in the appserver-watcher
process using the first example configuration below, or
add a scanner to restart the appserver upon changes to your webapp's code, as in the second example.
Implementing your own scanners is possible as well.
<scanners>
<scanner name="deployment" type="AppserverIo\Appserver\Core\Scanner\DeploymentScanner">
<params>
<param name="interval" type="integer">1</param>
<param name="extensionsToWatch" type="string">dodeploy, deployed</param>
</params>
<directories>
<directory>deploy</directory>
</directories>
</scanner>
<scanner name="webapps" type="AppserverIo\Appserver\Core\Scanner\RecursiveDirectoryScanner">
<params>
<param name="interval" type="integer">1</param>
<param name="extensionsToWatch" type="string">php</param>
</params>
<directories>
<directory>webapps</directory>
</directories>
</scanner>
<scanner name="logrotate" type="AppserverIo\Appserver\Core\Scanner\LogrotateScanner">
<params>
<param name="interval" type="integer">1</param>
<param name="extensionsToWatch" type="string">log</param>
<param name="maxFiles" type="integer">10</param>
<param name="maxSize" type="integer">1048576</param>
</params>
<directories>
<directory>var/log</directory>
</directories>
</scanner>
</scanners>
The Persistence-Container can also be used remotely. This allows you to distribute the components of your application across a network. Therefore, you need to configure a dedicated server thread for the Persistence-Container, which allows it connect over a streaming socket.
<server
name="persistence-container"
type="\AppserverIo\Server\Servers\MultiThreadedServer"
worker="\AppserverIo\Server\Workers\ThreadWorker"
socket="\AppserverIo\Server\Sockets\StreamSocket"
requestContext="\AppserverIo\Server\Contexts\RequestContext"
serverContext="\AppserverIo\Server\Contexts\ServerContext"
loggerName="System">
<params>
<param name="admin" type="string">info@appserver.io</param>
<param name="transport" type="string">tcp</param>
<param name="address" type="string">127.0.0.1</param>
<param name="port" type="integer">8585</param>
<param name="workerNumber" type="integer">8</param>
<param name="workerAcceptMin" type="integer">3</param>
<param name="workerAcceptMax" type="integer">8</param>
<param name="documentRoot" type="string">webapps</param>
<param name="directoryIndex" type="string">index.pc</param>
<param name="keepAliveMax" type="integer">64</param>
<param name="keepAliveTimeout" type="integer">5</param>
<param name="errorsPageTemplatePath" type="string">var/www/errors/error.phtml</param>
</params>
<environmentVariables>
<environmentVariable condition="" definition="LOGGER_ACCESS=Access" />
</environmentVariables>
<connectionHandlers>
<connectionHandler type="\AppserverIo\WebServer\ConnectionHandlers\HttpConnectionHandler" />
</connectionHandlers>
<accesses>
<!-- per default allow everything -->
<access type="allow">
<params>
<param name="X_REQUEST_URI" type="string">.*</param>
</params>
</access>
</accesses>
<!-- include of virtual host configurations -->
<xi:include href="conf.d/virtual-hosts.xml"/>
<modules>
<!-- REQUEST_POST hook -->
<module type="\AppserverIo\WebServer\Modules\AuthenticationModule"/>
<module type="\AppserverIo\WebServer\Modules\VirtualHostModule"/>
<module type="\AppserverIo\WebServer\Modules\EnvironmentVariableModule" />
<module type="\AppserverIo\WebServer\Modules\RewriteModule"/>
<module type="\AppserverIo\WebServer\Modules\DirectoryModule"/>
<module type="\AppserverIo\WebServer\Modules\AccessModule"/>
<module type="\AppserverIo\WebServer\Modules\CoreModule"/>
<module type="\AppserverIo\Appserver\PersistenceContainer\PersistenceContainerModule" />
<!-- RESPONSE_PRE hook -->
<module type="\AppserverIo\WebServer\Modules\DeflateModule"/>
<!-- RESPONSE_POST hook -->
<module type="\AppserverIo\Appserver\Core\Modules\ProfileModule"/>
</modules>
<fileHandlers>
<fileHandler name="persistence-container" extension=".pc" />
</fileHandlers>
</server>