Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed #940 Allow different environments aka stages with corresponding configuration #996

Merged
merged 4 commits into from
Apr 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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