Skip to content

Commit

Permalink
Merge pull request #996 from wagnert/master
Browse files Browse the repository at this point in the history
Closed #940 Allow different environments aka stages with corresponding configuration
  • Loading branch information
wagnert committed Apr 29, 2016
2 parents e194391 + 3e4a270 commit ae7172c
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 36 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

## Bugfixes

* None
* Closed [#859](https://github.com/appserver-io/appserver/issues/859) - Memory Leaks in Session Beans

## Features

* Add @Remove annotation to allow explicit desctruction of SFSBs
* Closed [#940](https://github.com/appserver-io/appserver/issues/940) - Allow different environments aka stages with corresponding configuration

# Version 1.1.1-beta9
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"appserver-io/single-app" : "~2.0",
"appserver-io/properties" : "~2.0",
"appserver-io/concurrency" : "0.3.*",
"appserver-io/description" : "~5.0",
"appserver-io/description" : "~6.0",
"appserver-io/configuration" : "~2.0",
"appserver-io/doppelgaenger" : "~1.0",
"appserver-io/routlt-project" : "~1.0"
Expand Down
20 changes: 20 additions & 0 deletions resources/schema/appserver.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,16 @@
<xs:field xpath="@extension" />
</xs:unique>
</xs:element>
<xs:element name="method-name">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="uuid" type="xs:string"/>
<xs:attribute ref="xml:base"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:element name="lifecycle-callback-method">
<xs:complexType>
<xs:simpleContent>
Expand Down Expand Up @@ -1568,6 +1578,7 @@
<xs:element ref="appserver:local"/>
<xs:element ref="appserver:remote"/>
<xs:element ref="appserver:init-on-startup" minOccurs="0"/>
<xs:element ref="appserver:remove-method" minOccurs="0"/>
<xs:element ref="appserver:post-construct" minOccurs="0"/>
<xs:element ref="appserver:pre-destroy" minOccurs="0"/>
<xs:element ref="appserver:post-detach" minOccurs="0"/>
Expand Down Expand Up @@ -1778,6 +1789,15 @@
<xs:attribute ref="xml:base"/>
</xs:complexType>
</xs:element>
<xs:element name="remove-method">
<xs:complexType>
<xs:choice>
<xs:element ref="appserver:method-name" maxOccurs="unbounded"/>
</xs:choice>
<xs:attribute name="uuid" type="xs:string"/>
<xs:attribute ref="xml:base"/>
</xs:complexType>
</xs:element>
<xs:element name="post-construct">
<xs:complexType>
<xs:choice>
Expand Down
81 changes: 77 additions & 4 deletions src/AppserverIo/Appserver/PersistenceContainer/BeanManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@

namespace AppserverIo\Appserver\PersistenceContainer;

use AppserverIo\Appserver\Core\AbstractEpbManager;
use AppserverIo\Collections\CollectionUtils;
use AppserverIo\Collections\CollectionInterface;
use AppserverIo\Storage\StorageInterface;
use AppserverIo\Appserver\Core\AbstractEpbManager;
use AppserverIo\Lang\Reflection\AnnotationInterface;
use AppserverIo\Psr\Application\ApplicationInterface;
use AppserverIo\Psr\EnterpriseBeans\BeanContextInterface;
Expand All @@ -34,11 +36,14 @@
use AppserverIo\Psr\EnterpriseBeans\Description\StatefulSessionBeanDescriptorInterface;
use AppserverIo\Psr\EnterpriseBeans\Description\StatelessSessionBeanDescriptorInterface;
use AppserverIo\Psr\EnterpriseBeans\Description\MessageDrivenBeanDescriptorInterface;
use AppserverIo\Appserver\Application\Interfaces\ManagerSettingsInterface;
use AppserverIo\Appserver\Application\Interfaces\ManagerSettingsAwareInterface;
use AppserverIo\Appserver\PersistenceContainer\Utils\SessionBeanUtil;
use AppserverIo\Appserver\PersistenceContainer\DependencyInjection\DirectoryParser;
use AppserverIo\Appserver\PersistenceContainer\DependencyInjection\DeploymentDescriptorParser;
use AppserverIo\Appserver\Application\Interfaces\ManagerSettingsInterface;
use AppserverIo\Appserver\DependencyInjectionContainer\Interfaces\ObjectManagerInterface;
use AppserverIo\RemoteMethodInvocation\RemoteMethodInterface;
use AppserverIo\RemoteMethodInvocation\FilterSessionPredicate;

/**
* The bean manager handles the message and session beans registered for the application.
Expand Down Expand Up @@ -443,7 +448,7 @@ public function destroyBeanInstance($instance)
{

// load the object manager
$objectManager = $this->getApplication()->search('ObjectManagerInterface');
$objectManager = $this->getApplication()->search(ObjectManagerInterface::IDENTIFIER);

// load the bean descriptor
$descriptor = $objectManager->getObjectDescriptors()->get(get_class($instance));
Expand All @@ -456,6 +461,74 @@ public function destroyBeanInstance($instance)
}
}

/**
* Invoke the passed remote method on the described session bean and return the result.
*
* @param \AppserverIo\RemoteMethodInvocation\RemoteMethodInterface $remoteMethod The remote method description
* @param \AppserverIo\Collections\CollectionInterface $sessions The collection with the sessions
*
* @return mixed The result of the remote method invocation
*/
public function invoke(RemoteMethodInterface $remoteMethod, CollectionInterface $sessions)
{

// prepare method name and parameters and invoke method
$className = $remoteMethod->getClassName();
$methodName = $remoteMethod->getMethodName();
$parameters = $remoteMethod->getParameters();
$sessionId = $remoteMethod->getSessionId();

// load the application instance
$application = $this->getApplication();

// try to load the session with the ID passed in the remote method
$session = CollectionUtils::find($sessions, new FilterSessionPredicate($sessionId));

// query whether the session is available or not
if ($session instanceof CollectionInterface) {
// query whether we already have an instance in the session container
if ($instance = $session->exists($className)) {
$instance = $session->get($className);
}
}

// load a fresh bean instance and add it to the session container
if ($instance == null) {
$instance = $application->search($className, array($sessionId, array($application)));
$session->add($className, $instance);
}

// invoke the remote method call on the local instance
$response = call_user_func_array(array($instance, $methodName), $parameters);

// load the object manager
$objectManager = $application->search(ObjectManagerInterface::IDENTIFIER);

// load the bean descriptor
$descriptor = $objectManager->getObjectDescriptors()->get(get_class($instance));

// initialize the flag to mark the instance to be re-attached
$attach = true;

// query if we've stateful session bean
if ($descriptor instanceof StatefulSessionBeanDescriptorInterface) {
// remove the SFSB instance if a remove method has been called
if ($descriptor->isRemoveMethod($methodName)) {
$this->removeStatefulSessionBean($sessionId, $descriptor->getClassName());
$session->remove($className);
$attach = false;
}
}

// re-attach the bean instance if necessary
if ($attach === true) {
$this->attach($instance, $sessionId);
}

// return the remote method call result
return $response;
}

/**
* Attaches the passed bean, depending on it's type to the container.
*
Expand All @@ -469,7 +542,7 @@ public function attach($instance, $sessionId = null)
{

// load the object manager
$objectManager = $this->getApplication()->search('ObjectManagerInterface');
$objectManager = $this->getApplication()->search(ObjectManagerInterface::IDENTIFIER);

// load the bean descriptor
$descriptor = $objectManager->getObjectDescriptors()->get(get_class($instance));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,10 @@ class DoctrineEntityManagerDecorator extends EntityManagerDecorator
public function __sleep()
{

// close the connection
$this->getWrapped()->getConnection()->close();
// query whether we've a wrapped instance
if ($wrapped = $this->getWrapped()) {
$wrapped->getConnection()->close();
}

// we want to serialize NOTHING
return array();
Expand All @@ -63,6 +65,20 @@ public function __destruct()
}
}

/**
* Returns the entity manager's connection instance.
*
* @return \Doctrine\DBAL\Connection The connection instance
*/
public function getConnection()
{

// query whether we've a wrapped instance
if ($wrapped = $this->getWrapped()) {
return $wrapped->getConnection();
}
}

/**
* Returns the wrapped entity manager instance.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
use AppserverIo\Psr\Servlet\Http\HttpServletResponseInterface;
use AppserverIo\RemoteMethodInvocation\RemoteMethodProtocol;
use AppserverIo\RemoteMethodInvocation\RemoteExceptionWrapper;
use AppserverIo\Collections\HashMap;
use AppserverIo\Psr\EnterpriseBeans\BeanContextInterface;

/**
* Valve implementation that will be executed by the servlet engine to handle
Expand Down Expand Up @@ -59,29 +61,15 @@ public function invoke(HttpServletRequestInterface $servletRequest, HttpServletR
/** @var \AppserverIo\Appserver\Application\Application $application */
$application = $servletRequest->getContext();

// prepare method name and parameters and invoke method
$className = $remoteMethod->getClassName();
$methodName = $remoteMethod->getMethodName();
$parameters = $remoteMethod->getParameters();
$sessionId = $remoteMethod->getSessionId();

// load the bean manager and the bean instance
$instance = $application->search($className, array($sessionId, array($application)));

// invoke the remote method call on the local instance
$response = call_user_func_array(array($instance, $methodName), $parameters);
// invoke the remote method and re-attach the bean instance to the container
$response = $application->search(BeanContextInterface::IDENTIFIER)->invoke($remoteMethod, new HashMap());

// serialize the remote method and write it to the socket
$servletResponse->appendBodyStream(RemoteMethodProtocol::pack($response));

// re-attach the bean instance in the container and unlock it
$application->search('BeanContextInterface')->attach($instance, $sessionId);

} catch (\Exception $e) {
// catch the exception and append it to the body stream
$servletResponse->appendBodyStream(
RemoteMethodProtocol::pack(RemoteExceptionWrapper::factory($e))
);
$servletResponse->appendBodyStream(RemoteMethodProtocol::pack(RemoteExceptionWrapper::factory($e)));
}

// finally dispatch this request, because we have finished processing it
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use AppserverIo\Appserver\Core\AbstractDaemonThread;
use AppserverIo\Psr\Application\ApplicationInterface;
use AppserverIo\Appserver\Naming\Utils\NamingDirectoryKeys;
use Psr\Log\LogLevel;

/**
* The garbage collector for the stateful session beans.
Expand Down Expand Up @@ -120,19 +121,25 @@ public function collectGarbage()
// initialize the timestamp with the actual time
$actualTime = time();

// load the map with the SFSB lifetime data
$lifetimeMap = $statefulSessionBeans->getLifetime();

// write a log message with size of SFSBs to be garbage collected
$this->log(LogLevel::DEBUG, sprintf('Found %d SFSBs be garbage collected', sizeof($lifetimeMap)));

// iterate over the applications sessions with stateful session beans
foreach ($statefulSessionBeans->getLifetime() as $identifier => $lifetime) {
foreach ($lifetimeMap as $identifier => $lifetime) {
// check the lifetime of the stateful session beans
if ($lifetime < $actualTime) {
// if the stateful session bean has timed out, remove it
$statefulSessionBeans->remove($identifier, array($beanManager, 'destroyBeanInstance'));
// write a log message
$this->getApplication()
->getNamingDirectory()
->search(NamingDirectoryKeys::SYSTEM_LOGGER)
->debug(sprintf('Successfully removed SFSB %s', $identifier));
$this->log(LogLevel::DEBUG, sprintf('Successfully removed SFSB %s', $identifier));
// reduce CPU load
usleep(1000);
} else {
// write a log message
$this->log(LogLevel::DEBUG, sprintf('Lifetime %s of SFSB %s is > %s', $lifetime, $identifier, $actualTime));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,12 +215,11 @@ public function remove($key, callable $beforeRemove = null)
}
// remove the item
unset($this->items[$key]);
// return the lifetime is set
// remove the lifetime if set
if (isset($this->lifetime[$key])) {
unset($this->lifetime[$key]);
}
// return the instance
return $this;
return;
} else {
throw new IndexOutOfBoundsException('Index ' . $key . ' out of bounds');
}
Expand All @@ -247,12 +246,11 @@ public function remove($key, callable $beforeRemove = null)
}
// remove the item
unset($this->items[$newKey]);
// return the lifetime is set
// remove the lifetime if set
if (isset($this->lifetime[$newKey])) {
unset($this->lifetime[$newKey]);
}
// returns the instance
return $this;
return;
} else {
throw new IndexOutOfBoundsException('Index ' . $newKey . ' out of bounds');
}
Expand Down

0 comments on commit ae7172c

Please sign in to comment.