/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.ericsson.ssa.config;
import com.ericsson.ssa.container.sim.ServletDispatcher;
import com.ericsson.ssa.container.sim.ApplicationDispatcher;
import com.ericsson.ssa.container.sim.SipServletFacade;
import com.ericsson.ssa.container.sim.SipServletWrapper;
import com.ericsson.ssa.dd.ConvergedDescriptor;
import org.jvnet.glassfish.comms.deployment.backend.SipApplication;
import org.jvnet.glassfish.comms.deployment.backend.SipApplicationListeners;
import com.ericsson.ssa.fm.FmEventSender;
import com.ericsson.ssa.sip.ConvergedHttpSessionFacade;
import com.ericsson.ssa.sip.PersistentSipSessionManagerBase;
import com.ericsson.ssa.sip.SipApplicationSessionBase;
import com.ericsson.ssa.sip.SipApplicationSessionImpl;
import com.ericsson.ssa.sip.SipApplicationSessionStore;
import com.ericsson.ssa.sip.SipFactoryImpl;
import com.ericsson.ssa.sip.SipSessionDialogImpl;
import com.ericsson.ssa.sip.SipSessionManager;
import com.ericsson.ssa.sip.SipSessionManagerBase;
import com.ericsson.ssa.sip.SipSessionsUtilImpl;
import com.ericsson.ssa.sip.timer.ServletTimerImpl;
import com.ericsson.ssa.sip.timer.TimerServiceImpl;
import com.ericsson.ssa.sip.persistence.ReplicationUnitOfWork;
import com.sun.enterprise.config.serverbeans.J2eeApplication;
import com.sun.enterprise.config.serverbeans.SipContainerAvailability;
import com.sun.enterprise.deployment.WebBundleDescriptor;
import com.sun.enterprise.deployment.runtime.web.SessionConfig;
import com.sun.enterprise.deployment.runtime.web.SessionManager;
import com.sun.enterprise.web.PersistenceStrategyBuilder;
import com.sun.enterprise.web.PersistenceStrategyBuilderFactory;
import com.sun.enterprise.web.WebContainer;
import com.sun.enterprise.web.WebModule;
import com.sun.enterprise.web.WebModuleConfig;
import com.sun.enterprise.web.session.PersistenceType;
import org.apache.catalina.Container;
import org.apache.catalina.Globals;
import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleException;
import org.apache.catalina.Session;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.ApplicationContext;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.session.StandardSession;
import org.apache.catalina.session.StandardSessionFacade;
import org.apache.coyote.tomcat5.CoyoteRequest;
import org.apache.coyote.tomcat5.CoyoteResponse;
import org.jvnet.glassfish.comms.deployment.backend.SipBundleDescriptor;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jvnet.glassfish.comms.util.LogUtil;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.sip.SipServlet;
import org.jvnet.glassfish.comms.deployment.backend.SipApplicationBase;
import org.jvnet.glassfish.comms.extensions.Extension;
import org.glassfish.comms.api.datacentric.DataCentricUtil;
import com.ericsson.ssa.container.datacentric.DataCentricUtilHolder;
/**
* Extension of the Tomcat StandardContext that implements the ConvergedContext
* interface relevant for SIP applications. The ConvergedContextImpl class is
* configured by the ConvegedContextConfig class.
*
* @author epiesan
* @since Feb 20, 2006
*/
@SuppressWarnings("serial")
public class ConvergedContextImpl extends WebModule implements ConvergedContext {
private static final Logger logger = LogUtil.SIP_LOGGER.getLogger();
private static HashMap<J2eeApplication, Set> appRegistry =
new HashMap<J2eeApplication, Set>();
private SipApplication sipApplication;
private ServletDispatcher dispatcher;
private SipFactoryFacade sipFactory;
private SipApplicationListeners sipApplicationListeners;
private SipSessionsUtilImpl sipSessionsUtil;
//private ConvergedApplicationContext context;
private SipSessionManager sipSessionManager;
private String appName;
public ConvergedContextImpl(WebContainer wc) {
super(wc);
}
/**
* Starts this converged context.
*/
public synchronized void start() throws LifecycleException {
Extension.getInstance().preStart(this);
super.start();
J2eeApplication j2eeAppBean = getApplicationBean();
if (j2eeAppBean != null) {
// This converged context has been embedded in an EAR file.
// Make it accessible to the sibling converged contexts that
// are embedded in the same EAR file, by registering it with
// the application registry.
Set siblings = null;
synchronized (appRegistry) {
siblings = appRegistry.get(j2eeAppBean);
if (siblings == null) {
siblings = Collections.synchronizedSet(
new HashSet());
appRegistry.put(j2eeAppBean, siblings);
}
}
siblings.add(this);
}
}
/**
* Stops this converged context.
*/
public void stop() throws LifecycleException {
super.stop();
J2eeApplication j2eeAppBean = getApplicationBean();
if (j2eeAppBean != null) {
// This converged context has been embedded in an EAR file.
// Unregister it from the application registry.
synchronized (appRegistry) {
Set siblings = appRegistry.get(j2eeAppBean);
if (siblings != null) {
siblings.remove(this);
if (siblings.size() == 0) {
appRegistry.remove(j2eeAppBean);
}
}
}
}
}
/**
* Destroys this converged context.
*/
public void destroy() throws Exception {
super.destroy();
SipSessionManager sipSessMgr = getSipSessionManager();
if (sipSessMgr != null) {
sipSessMgr.release();
sipSessMgr = null;
}
sipSessionManager = null; // required to release sessionmanager.
lifecycle = null; // required to release servletdispatcher.
pipeline = null; // required to release servletdispatcher.
}
/**
* Executes a periodic task.
*/
public void backgroundProcess() {
// Purge any expired HTTP sessions from both the active cache and
// the persistent store (if any)
super.backgroundProcess();
SipSessionManager sipSessMgr = getSipSessionManager();
if (sipSessMgr != null && count == 0) {
if (sipSessMgr instanceof PersistentSipSessionManagerBase) {
// Remove any expired SIP artifacts from their respective
// persistent stores.
// There is no need to remove them from any of the active
// caches, since they are automatically removed from them
// upon their invalidation or expiration.
((PersistentSipSessionManagerBase) sipSessMgr).backgroundProcess();
}
}
}
/**
* Gets the set containing this converged context and all its
* sibling converged contexts that have been embedded in the same
* EAR file.
*
* @return The set containing this converged context and all its
* sibling converged contexts that have been embedded in the same
* EAR file, or null if this converged context is standalone
*/
public Set getSiblingContexts() {
Set siblings = null;
J2eeApplication j2eeAppBean = getApplicationBean();
if (j2eeAppBean != null) {
synchronized (appRegistry) {
siblings = appRegistry.get(j2eeAppBean);
}
}
return siblings;
}
/**
* Load and initialize all servlets marked "load on startup" in the web
* application deployment descriptor. Also initializes all SIP servlets that
* have been marked "load on startup".
*
* @param children
* Array of wrappers for all currently defined servlets (including
* those not declared load on startup)
*/
@Override
public void loadOnStartup(Container[] children) throws LifecycleException {
try {
loadOnStartupHttp(children);
loadOnStartupSip();
} catch (ServletException ex) {
throw new LifecycleException(ex);
}
}
private void loadOnStartupSip() throws ServletException {
if (dispatcher != null) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Loading SIP servlets on startup");
}
dispatcher.loadServletsMarkedOnStartup();
}
}
private void loadOnStartupHttp(Container[] children) throws ServletException {
// Collect "load on startup" servlets that need to be initialized
TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<Integer, ArrayList<Wrapper>>();
for (int i = 0; i < children.length; i++) {
Wrapper wrapper = (Wrapper) children[i];
int loadOnStartup = wrapper.getLoadOnStartup();
if (loadOnStartup < 0) {
continue;
}
if (loadOnStartup == 0) { // Arbitrarily put them last
loadOnStartup = Integer.MAX_VALUE;
}
Integer key = new Integer(loadOnStartup);
ArrayList<Wrapper> list = map.get(key);
if (list == null) {
list = new ArrayList<Wrapper>();
map.put(key, list);
}
list.add(wrapper);
}
// Load the collected "load on startup" servlets
Iterator keys = map.keySet().iterator();
while (keys.hasNext()) {
Integer key = (Integer) keys.next();
ArrayList list = (ArrayList) map.get(key);
Iterator wrappers = list.iterator();
while (wrappers.hasNext()) {
Wrapper wrapper = (Wrapper) wrappers.next();
try {
wrapper.load();
} catch (ServletException e) {
// NOTE: load errors (including a servlet that throws
// UnavailableException from the init() method) are NOT
// fatal to application startup
logger.log(Level.SEVERE, "sipstack.servlet_init_error", wrapper.getName());
logger.log(Level.SEVERE, "" + StandardWrapper.getRootCause(e).getMessage(), StandardWrapper.getRootCause(e));
// Generate a servlet initialization failure notification.
FmEventSender.servletInitializationFailed(getAppName(),
wrapper.getName(), e.getMessage());
throw e;
}
}
}
}
/**
* The method will allow another object to add an attribute into the
* ServletContext and should only be called in the handler for the
* BEFORE_DEPLOYMENT_EVENT event.
*
* @param attributeName
* The name of the context attribute.
* @param attributeValue
* The value of the context attribute.
*/
public void addAttributeToServletContext(String attributeName,
Object attributeValue) {
if ((attributeName != null) && (attributeValue != null)) {
ServletContext servletContext = getServletContext();
servletContext.setAttribute(attributeName, attributeValue);
}
}
public ServletDispatcher getDispatcher() {
return dispatcher;
}
public void setDispatcher(ServletDispatcher dispatcher) {
this.dispatcher = dispatcher;
}
public SipApplication getSipApplication() {
return sipApplication;
}
public void setSipApplication(SipApplication sipApplication) {
this.sipApplication = sipApplication;
if (sipApplication != null) {
SipSessionManagerBase mgr = (SipSessionManagerBase)
getSipSessionManager();
if (mgr != null) {
mgr.setSessionTimeout(sipApplication.getSessionConfigTimeout());
}
}
}
public SipApplicationListeners getSipApplicationListeners() {
return sipApplicationListeners;
}
public void setSipApplicationListeners(
SipApplicationListeners sipApplicationListeners) {
this.sipApplicationListeners = sipApplicationListeners;
}
public SipFactoryFacade getSipFactory() {
return sipFactory;
}
public void setSipFactory(SipFactoryFacade sipFactory) {
this.sipFactory = sipFactory;
}
/**
* Sets the given SipSessionsUtil on this converged context.
*
* @param util The SipSessionsUtil
*/
void setSipSessionsUtil(SipSessionsUtilImpl util) {
this.sipSessionsUtil = util;
}
/**
* Gets the SipSessionsUtil of this converged context.
*
* @return The SipSessionsUtil of this converged context
*/
public SipSessionsUtilImpl getSipSessionsUtil() {
return sipSessionsUtil;
}
/**
* Get base path.
*/
private String getBasePath() {
String docBase = null;
Container container = this;
while (container != null) {
if (container instanceof Host) {
break;
}
container = container.getParent();
}
File file = new File(getDocBase());
if (!file.isAbsolute()) {
if (container == null) {
docBase = (new File(engineBase(), getDocBase())).getPath();
} else {
// Use the "appBase" property of this container
String appBase = ((Host) container).getAppBase();
file = new File(appBase);
if (!file.isAbsolute()) {
file = new File(engineBase(), appBase);
}
docBase = (new File(file, getDocBase())).getPath();
}
} else {
docBase = file.getPath();
}
return docBase;
}
/**
* Return the servlet context for which this Context is a facade.
*/
public ServletContext getServletContext() {
if (context == null) {
context = new ConvergedApplicationContext(getBasePath(), this);
if (getAltDDName() != null) {
context.setAttribute(Globals.ALT_DD_ATTR, getAltDDName());
}
}
return (((ConvergedApplicationContext) context).getConvergedFacade());
}
/**
* Returns a facade for the given session.
*
* @return A facade for the given session
*/
public StandardSessionFacade createSessionFacade(StandardSession session) {
return new ConvergedHttpSessionFacade(session, this);
}
/**
* Starts the HTTP session and SIP session managers of this converged
* context.
*/
protected void managerStart() throws LifecycleException {
// Start the HTTP session manager
super.managerStart();
// Start the SIP session manager
if ((sipSessionManager != null) &&
(sipSessionManager instanceof Lifecycle)) {
((Lifecycle) sipSessionManager).start();
}
}
/**
* Stops the HTTP session and SIP session managers of this converged
* context.
*/
protected void managerStop() throws LifecycleException {
// Stop the HTTP session manager
super.managerStop();
// Stop the SIP session manager
if ((sipSessionManager != null) &&
(sipSessionManager instanceof Lifecycle)) {
((Lifecycle) sipSessionManager).stop();
}
}
/**
* Gets the SIP session manager of this converged context.
*
* @return The SIP session manager of this converged context.
*/
public SipSessionManager getSipSessionManager() {
try {
readLock.lock();
if (sipSessionManager != null) {
return sipSessionManager;
}
} finally {
readLock.unlock();
}
return null;
}
/**
* Sets the SIP session manager for this converged context
*
* @param sipSessionManager The new SIP session manager
*/
public void setSipSessionManager(SipSessionManager sipSessionManager) {
SipSessionManager oldSipSessionManager;
try {
writeLock.lock();
oldSipSessionManager = this.sipSessionManager;
if (oldSipSessionManager == sipSessionManager) {
return;
}
this.sipSessionManager = sipSessionManager;
// Stop the old SipSessionManager
if (started && (oldSipSessionManager != null) &&
(oldSipSessionManager instanceof Lifecycle)) {
try {
((Lifecycle) oldSipSessionManager).stop();
} catch (LifecycleException e) {
logger.log(Level.SEVERE, "sipstack.session_manager_stop", getAppName());
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
if (sipSessionManager != null) {
sipSessionManager.setContext(this);
// Start the new SipSessionManager
if (started && (sipSessionManager instanceof Lifecycle)) {
try {
((Lifecycle) sipSessionManager).start();
} catch (LifecycleException e) {
logger.log(Level.SEVERE, "sipstack.session_manager_start", getAppName());
logger.log(Level.SEVERE, e.getMessage(), e);
}
}
}
} finally {
writeLock.unlock();
}
// Report this property change to interested listeners
support.firePropertyChange("sipSessionManager", oldSipSessionManager,
this.sipSessionManager);
}
/**
* Initializes the given session by storing properties of the given
* request (more specifically, the request URL and the values of the
* bekey and beroute request headers) as notes in it.
*
* This method also initializes the unit of work.
*
* @param request The request object whose properties to store
* @param session The session on which to store the request info as notes
*/
public void initializeSession(CoyoteRequest request, Session session) {
session.setNote(Constants.REQUEST_URL_SESSION_NOTE,
request.getRequestURL().toString());
String value = request.getHeader(Constants.PROXY_BEROUTE_HEADER);
if (value != null) {
session.setNote(Constants.BEROUTE_SESSION_NOTE, value);
}
Object o = request.getAttribute(
Globals.SESSION_VERSIONS_REQUEST_ATTRIBUTE);
if (o != null) {
session.setNote(Constants.SESSION_VERSIONS_SESSION_NOTE, o);
}
}
/**
* SailFin extension.
*
* Allows context to implement additional tasks at the beginning of its
* pipeline invocation.
*
* Creates Unit-of-work and locks SAS if present.
*/
public void beginPipelineInvoke(Session sess) {
// Initialize new unit-of-work and store it as threadlocal
new ReplicationUnitOfWork();
if (sess == null) {
return;
}
// Lock SAS if one is being referenced
ConvergedHttpSessionFacade chs = (ConvergedHttpSessionFacade)
createSessionFacade((StandardSession) sess);
if (chs != null) {
SipApplicationSessionImpl sas = (SipApplicationSessionImpl)
chs.getApplicationSession(false);
if (sas != null) {
ReplicationUnitOfWork uow =
ReplicationUnitOfWork.getThreadLocalUnitOfWork();
if (uow != null) {
uow.lockApplicationSession(sas);
} else {
throw new IllegalStateException(
"Missing Unit-of-work when there should be one");
}
}
}
}
/**
* SailFin extension.
*
* Allows context to implement additional tasks at the end of its
* pipeline invocation.
*
* Unlocks Unit-of-work.
*/
public void endPipelineInvoke() {
ReplicationUnitOfWork uow =
ReplicationUnitOfWork.getThreadLocalUnitOfWork();
if (uow != null) {
uow.unlock();
} else {
throw new IllegalStateException(
"Missing Unit-of-work when there should be one");
}
}
/**
* Configures the HTTP and SIP session managers of this converged context,
* by combining information from domain.xml as well as from the converged
* context's web and SIP related deployment descriptors
*/
protected void configureSessionManager(SessionManager httpSmBean,
WebBundleDescriptor wbd, WebModuleConfig wmInfo) {
SessionConfig sipSessionConfig =
((SipBundleDescriptor) wbd).getSunSipSessionConfig();
SessionManager sipSmBean = null;
if (sipSessionConfig != null) {
sipSmBean = sipSessionConfig.getSessionManager();
}
ConvergedSessionManagerConfigurationHelper configHelper =
new ConvergedSessionManagerConfigurationHelper(this,
httpSmBean, sipSmBean, wbd, wmInfo);
/*
* Configure HTTP session manager
*/
PersistenceType httpType = configHelper.getPersistenceType();
String httpFrequency = configHelper.getPersistenceFrequency();
String httpScope = configHelper.getPersistenceScope();
PersistenceStrategyBuilderFactory httpFactory =
new PersistenceStrategyBuilderFactory();
PersistenceStrategyBuilder httpBuilder =
httpFactory.createPersistenceStrategyBuilder(httpType.getType(),
httpFrequency, httpScope, this);
httpBuilder.setLogger(Logger.getLogger(LogUtil.SIP_LOG_DOMAIN));
httpBuilder.setIsConverged(true);
httpBuilder.initializePersistenceStrategy(this, httpSmBean);
/*
* Configure SIP session manager
*/
PersistenceType sipType = configHelper.getSipPersistenceType();
String sipFrequency = configHelper.getSipPersistenceFrequency();
String sipScope = configHelper.getSipPersistenceScope();
logger.log(Level.INFO, "Application " + getName() +
" configured with SIP persistence type: " + sipType.getType() +
", frequency: " + sipFrequency + ", and scope: " + sipScope);
SipPersistenceStrategyBuilderFactory sipFactory =
new SipPersistenceStrategyBuilderFactory();
SipPersistenceStrategyBuilder sipBuilder =
sipFactory.createPersistenceStrategyBuilder(sipType.getType(),
sipFrequency, sipScope, this);
sipBuilder.setLogger(Logger.getLogger(LogUtil.SIP_LOG_DOMAIN));
sipBuilder.initializePersistenceStrategy(this, sipSmBean,
configHelper.getSipContainerAvailability());
}
/**
* Overrides the setAvailable method of the Context interface
* sets the availablity of the web module and also
* makes the ServletDispatcher for this application
* available or unavailable
* @param the flag the sets the availability
*/
public void setAvailable(boolean isAvailable) {
super.setAvailable(isAvailable);
if(this.getDispatcher() == null ) {
return;
}
if(isAvailable) {
logger.log(Level.FINE, "Enabling the application "+
getName());
if(ApplicationDispatcher.getInstance().
getServletDispatcher(getAppName()) == null) {
ApplicationDispatcher.getInstance().addServletDispatcher(getAppName(),
this.getDispatcher());
}
} else {
logger.log(Level.FINE, "Disabling the application "+
getName());
if(ApplicationDispatcher.getInstance().
getServletDispatcher(getAppName()) != null) {
ApplicationDispatcher.getInstance().removeServletDispatcher(getAppName());
}
}
}
/**
* Getter method for the app-name
* @return String the appname value for this application
*/
public String getAppName() {
return this.appName;
}
/**
* Setter method for the app-name
* @param String the appname value for this application
*/
public void setAppName(String appName) {
this.appName = appName;
}
/**
* SailFin extension.
*
* @return true if this context is converged, false otherwise
*/
public boolean isConverged() {
/*
* TODO: This method should follow the approach taken by
* ConvergedApplicationContext to determine whether the application
* is converged or pure SIP, instead of always returning true.
* Once that is done, ConvergedApplicationContext could simply
* rely on this method.
*/
return true;
}
// Make sure that this method is called only during or after the session manager
// is started. This method will return 'false' if it is called before
// manager.start() is called.
public boolean containsHttpServlets() {
if(sipApplication != null) {
Object o = sipApplication.getReferringDescriptor();
if(o instanceof SipBundleDescriptor) {
return ((SipBundleDescriptor)o).containsHTTPServlets();
}
}
return false;
}
public class ConvergedApplicationContext extends ApplicationContext {
private boolean isConverged = false;
public ConvergedApplicationContext(String basePath,
ConvergedContextImpl context) {
super(basePath, context);
if (context != null) {
SipApplication sa = context.getSipApplication();
if (sa != null) {
Object o = sa.getReferringDescriptor();
if (o instanceof SipBundleDescriptor) {
isConverged = ((SipBundleDescriptor)o).isConverged();
}
}
}
}
/**
* Return a <code>RequestDispatcher</code> object that acts as a
* wrapper for the named servlet.
*
* @param name
* Name of the servlet for which a dispatcher is requested
*/
public RequestDispatcher getNamedDispatcher(String name) {
// Validate the name argument
if (name == null) {
return (null);
}
Container child = findChild(name);
if (child instanceof SipServletWrapper) {
// Handles SIP requests/responses
return new SipRequestDispatcher(name,
SipFactoryImpl.getInstance().getServiceHandler());
} else {
// Handles HTTP requests
return super.getNamedDispatcher(name);
}
}
public RequestDispatcher getRequestDispatcher(String path) {
if (isConverged) {
return super.getRequestDispatcher(path);
} else {
/*
* Pure SIP application, which means SIP Servlet 1.1
* specification, section 3.4 ("Context Path") applies:
* As SIP URIs do not have a notion of paths, the following
* ServletContext methods have no meaning for SIP-only
* servlet applications/containers and must return null:
* [...]
* RequestDispatcher getRequestDispatcher(String path);
*/
return null;
}
}
public ServletContext getConvergedFacade() {
return getFacade();
}
public Object getAttribute(String name) {
if(name.equals(DataCentricUtil.CONTEXT_ATTRIBUTE_NAME)){
return DataCentricUtilHolder.getInstance();
}
return super.getAttribute(name);
}
}
}