blob: e488919cf0bed173e56d94e6504d45bf3cbb70db [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package flex.messaging.factories;
import flex.messaging.FlexFactory;
import flex.messaging.DestructibleFlexFactory;
import flex.messaging.FlexSession;
import flex.messaging.FlexContext;
import flex.messaging.FactoryInstance;
import flex.messaging.MessageBroker;
import flex.messaging.FlexSessionListener;
import flex.messaging.config.ConfigMap;
import flex.messaging.config.ConfigurationException;
import flex.messaging.services.ServiceException;
import flex.messaging.config.ConfigurationManager;
import flex.messaging.log.Log;
import flex.messaging.util.StringUtils;
import flex.messaging.util.ExceptionUtil;
import javax.servlet.ServletContext;
/**
* This class implements the FlexFactory interface to constructs Flex messaging
* components. The JavaFactory uses the class name, specified as the source
* attribute to determine the class for instances. The scope attribute can be one of
* session, application or request to determine its lifecycle. If you
* use application or session, you can specify the optional attribute-id
* parameter to control the name of the key for storing the component. Two destinations
* using the same attribute-id will use the same component. The component is stored
* in the ServletContext (for application scoped components) and in the
* session (for session scoped components) so you can use these components in your
* JSP as well.
*/
public class JavaFactory implements FlexFactory, DestructibleFlexFactory
{
private static final String ATTRIBUTE_ID = "attribute-id";
private static final int SINGLETON_ERROR = 10656;
private static final int SESSION_NOT_FOUND = 10652;
private static final int INVALID_CLASS_FOUND = 10654;
/**
*
* Default constructor
*/
public JavaFactory()
{
}
/**
* This method can be used to provide additional configuration parameters
* for the initializing this factory instance itself.
*/
public void initialize(String id, ConfigMap configMap) {}
/**
* This method is called when we initialize the definition of an instance which
* will be looked up by this factory. It should validate that the properties
* supplied are valid to define an instance. Any valid properties used for
* this configuration must be accessed to avoid warnings about unused
* configuration elements. If your factory is only used for application
* scoped components, you do not need to implement this method as the lookup
* method itself can be used to validate its configuration.
*/
public FactoryInstance createFactoryInstance(String id, ConfigMap properties)
{
JavaFactoryInstance instance = new JavaFactoryInstance(this, id, properties);
if (properties == null)
{
// Use destination id as the default attribute id to prevent unwanted sharing.
instance.setSource(instance.getId());
instance.setScope(SCOPE_REQUEST);
instance.setAttributeId(id);
}
else
{
instance.setSource(properties.getPropertyAsString(SOURCE, instance.getId()));
instance.setScope(properties.getPropertyAsString(SCOPE, SCOPE_REQUEST));
// Use destination id as the default attribute id to prevent unwanted sharing.
instance.setAttributeId(properties.getPropertyAsString(ATTRIBUTE_ID, id));
}
if (instance.getScope().equalsIgnoreCase(SCOPE_APPLICATION))
{
try
{
MessageBroker mb = FlexContext.getMessageBroker();
ServletContext ctx = mb != null? mb.getServletContext() : null;
if (ctx == null) // Should not be the case; just in case.
return instance;
synchronized (ctx)
{
Object inst = ctx.getAttribute(instance.getAttributeId());
if (inst == null)
{
inst = instance.createInstance();
ctx.setAttribute(instance.getAttributeId(), inst);
}
else
{
Class configuredClass = instance.getInstanceClass();
Class instClass = inst.getClass();
if (configuredClass != instClass &&
!configuredClass.isAssignableFrom(instClass))
{
ServiceException e = new ServiceException();
e.setMessage(INVALID_CLASS_FOUND, new Object[] {
instance.getAttributeId(), "application", instance.getId(),
instance.getInstanceClass(), inst.getClass()});
e.setCode("Server.Processing");
throw e;
}
}
instance.applicationInstance = inst;
// increment attribute-id reference count on MB
mb.incrementAttributeIdRefCount(instance.getAttributeId());
}
}
catch (Throwable t)
{
ConfigurationException ex = new ConfigurationException();
ex.setMessage(SINGLETON_ERROR, new Object[] { instance.getSource(), id });
ex.setRootCause(t);
if (Log.isError())
Log.getLogger(ConfigurationManager.LOG_CATEGORY).error(ex.getMessage() + StringUtils.NEWLINE + ExceptionUtil.toString(t));
throw ex;
}
}
else if(instance.getScope().equalsIgnoreCase(SCOPE_SESSION))
{
// increment attribute-id reference count on MB for Session scoped instances
MessageBroker mb = FlexContext.getMessageBroker();
if (mb != null)
mb.incrementAttributeIdRefCount(instance.getAttributeId());
}
return instance;
}
/**
* Returns the instance specified by the source
* and properties arguments. For the factory, this may mean
* constructing a new instance, optionally registering it in some other
* name space such as the session or JNDI, and then returning it
* or it may mean creating a new instance and returning it.
* This method is called for each request to operate on the
* given item by the system so it should be relatively efficient.
* <p>
* If your factory does not support the scope property, it report an error
* if scope is supplied in the properties for this instance.
* </p>
*
* @param inst the FactoryInstance to lookup.
* @return the constructed and initialized component for this factory instance.
*/
public Object lookup(FactoryInstance inst)
{
JavaFactoryInstance factoryInstance = (JavaFactoryInstance) inst;
Object instance;
if (factoryInstance.getScope().equalsIgnoreCase(SCOPE_APPLICATION))
{
instance = factoryInstance.applicationInstance;
}
else if (factoryInstance.getScope().equalsIgnoreCase(SCOPE_SESSION))
{
// See if an instance already exists in this http session first
FlexSession session = FlexContext.getFlexSession();
if (session != null)
{
instance = session.getAttribute(factoryInstance.getAttributeId());
if (instance != null)
{
Class configuredClass = factoryInstance.getInstanceClass();
Class instClass = instance.getClass();
if (configuredClass != instClass &&
!configuredClass.isAssignableFrom(instClass))
{
ServiceException e = new ServiceException();
e.setMessage(INVALID_CLASS_FOUND, new Object[] {
factoryInstance.getAttributeId(),
"session",
factoryInstance.getId(),
factoryInstance.getInstanceClass(), instance.getClass()});
e.setCode("Server.Processing");
throw e;
}
}
else
{
// none exists - create it the first time for each session
instance = factoryInstance.createInstance();
session.setAttribute(factoryInstance.getAttributeId(), instance);
}
}
else
instance = null;
if (instance == null)
{
ServiceException e = new ServiceException();
e.setMessage(SESSION_NOT_FOUND, new Object[] {factoryInstance.getId()});
e.setCode("Server.Processing");
throw e;
}
}
else
{
instance = factoryInstance.createInstance();
}
return instance;
}
/**
* This method is called when a component using this factory is being destroyed.
* When appropriate, it frees up resources that were used by the factory instance
* and are no longer needed
*
* @param inst The FactoryInstance to be cleaned up
*/
public void destroyFactoryInstance(FactoryInstance inst)
{
JavaFactoryInstance factoryInstance = (JavaFactoryInstance) inst;
// if we are stopping a destination with an Application or Session scoped assembler, we may
// have to remove the assembler from the ServletContext or Session
if (factoryInstance != null)
{
MessageBroker mb = FlexContext.getMessageBroker();
String attributeId = factoryInstance.getAttributeId();
if (FlexFactory.SCOPE_APPLICATION.equals(factoryInstance.getScope()))
{
ServletContext ctx = mb.getServletContext();
if (ctx == null) // Should never be the case, but just in case.
return;
synchronized (ctx)
{
// remove from ServletContext if reference count is zero
int refCount = (mb != null) ? mb.decrementAttributeIdRefCount(attributeId) : 0;
if (refCount <= 0)
{
// remove assembler from servlet context
ctx.removeAttribute(attributeId);
}
}
}
else if (FlexFactory.SCOPE_SESSION.equals(factoryInstance.getScope()))
{
FlexSession session = FlexContext.getFlexSession();
// if this is being stopped during runtime config, we should have a session available to us
// However, if this is being stopped on MessageBroker shutdown, we will not have a session
// but do not need to worry about clean up in that case as the entire session will be cleaned up
if (session == null)
return;
// remove from Session if reference count is zero
int refCount = (mb != null) ? mb.decrementAttributeIdRefCount(attributeId) : 0;
if (refCount <= 0)
{
// remove assembler from servlet context
session.removeAttribute(attributeId);
}
}
// Remove this instance from Session created listeners
// Only helps if listener was created by the factory, but this is common (aka assembler classes)
if (factoryInstance.applicationInstance instanceof FlexSessionListener)
{
FlexSession.removeSessionCreatedListener((FlexSessionListener) factoryInstance.applicationInstance);
}
}
}
}