diff --git a/spring-context/src/main/java/org/springframework/context/ApplicationContext.java b/spring-context/src/main/java/org/springframework/context/ApplicationContext.java
index 44fe37f31a..85336bc43a 100644
--- a/spring-context/src/main/java/org/springframework/context/ApplicationContext.java
+++ b/spring-context/src/main/java/org/springframework/context/ApplicationContext.java
@@ -63,6 +63,12 @@ public interface ApplicationContext extends EnvironmentCapable, ListableBeanFact
*/
String getId();
+ /**
+ * Return a name for the deployed application that this context belongs to.
+ * @return a name for the deployed application, or the empty String by default
+ */
+ String getApplicationName();
+
/**
* Return a friendly name for this context.
* @return a display name for this context (never null
)
diff --git a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
index dead77434a..0f293027a3 100644
--- a/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
+++ b/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java
@@ -32,6 +32,7 @@ import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.DisposableBean;
@@ -242,14 +243,14 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
this.id = id;
}
- /**
- * Return the unique id of this application context.
- * @return the unique id of the context, or null
if none
- */
public String getId() {
return this.id;
}
+ public String getApplicationName() {
+ return "";
+ }
+
/**
* Set a friendly name for this context.
* Typically done during initialization of concrete context implementations.
@@ -283,7 +284,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
*/
public ConfigurableEnvironment getEnvironment() {
if (this.environment == null) {
- this.environment = this.createEnvironment();
+ this.environment = createEnvironment();
}
return this.environment;
}
@@ -397,7 +398,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
if (parent != null) {
Environment parentEnvironment = parent.getEnvironment();
if (parentEnvironment instanceof ConfigurableEnvironment) {
- this.getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);
+ getEnvironment().merge((ConfigurableEnvironment)parentEnvironment);
}
}
}
@@ -513,7 +514,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
- this.getEnvironment().validateRequiredProperties();
+ getEnvironment().validateRequiredProperties();
}
/**
@@ -549,7 +550,7 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver());
- beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, this.getEnvironment()));
+ beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
@@ -940,6 +941,9 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
// Publish the final event.
publishEvent(new ContextRefreshedEvent(this));
+
+ // Participate in LiveBeansView MBean, if active.
+ LiveBeansView.registerApplicationContext(this);
}
/**
@@ -1033,6 +1037,8 @@ public abstract class AbstractApplicationContext extends DefaultResourceLoader
logger.info("Closing " + this);
}
+ LiveBeansView.unregisterApplicationContext(this);
+
try {
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
diff --git a/spring-context/src/main/java/org/springframework/context/support/LiveBeansView.java b/spring-context/src/main/java/org/springframework/context/support/LiveBeansView.java
new file mode 100644
index 0000000000..6a6291cb33
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/support/LiveBeansView.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed 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 org.springframework.context.support;
+
+import java.lang.management.ManagementFactory;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import javax.management.MBeanServer;
+import javax.management.ObjectName;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.ApplicationContextException;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+/**
+ * Adapter for live beans view exposure, building a snapshot of current beans
+ * and their dependencies from either a local ApplicationContext (with a
+ * local LiveBeansView bean definition) or all registered ApplicationContexts
+ * (driven by the "spring.liveBeansView.mbean" environment property).
+ *
+ *
Note: This feature is still in beta and primarily designed for use with
+ * SpringSource Tool Suite 3.1.
+ *
+ * @author Juergen Hoeller
+ * @since 3.2
+ * @see #getSnapshotAsJson()
+ * @see org.springframework.web.context.support.LiveBeansViewServlet
+ */
+public class LiveBeansView implements LiveBeansViewMBean, ApplicationContextAware {
+
+ public static final String MBEAN_DOMAIN_PROPERTY_NAME = "spring.liveBeansView.mbeanDomain";
+
+ public static final String MBEAN_APPLICATION_KEY = "application";
+
+ private static final Set applicationContexts =
+ new LinkedHashSet();
+
+ static void registerApplicationContext(ConfigurableApplicationContext applicationContext) {
+ String mbeanDomain = applicationContext.getEnvironment().getProperty(MBEAN_DOMAIN_PROPERTY_NAME);
+ if (mbeanDomain != null) {
+ synchronized (applicationContexts) {
+ if (applicationContexts.isEmpty()) {
+ try {
+ MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+ server.registerMBean(new LiveBeansView(),
+ new ObjectName(mbeanDomain, MBEAN_APPLICATION_KEY, applicationContext.getApplicationName()));
+ }
+ catch (Exception ex) {
+ throw new ApplicationContextException("Failed to register LiveBeansView MBean", ex);
+ }
+ }
+ applicationContexts.add(applicationContext);
+ }
+ }
+ }
+
+ static void unregisterApplicationContext(ConfigurableApplicationContext applicationContext) {
+ synchronized (applicationContexts) {
+ if (applicationContexts.remove(applicationContext) && applicationContexts.isEmpty()) {
+ try {
+ MBeanServer server = ManagementFactory.getPlatformMBeanServer();
+ String mbeanDomain = applicationContext.getEnvironment().getProperty(MBEAN_DOMAIN_PROPERTY_NAME);
+ server.unregisterMBean(new ObjectName(mbeanDomain, MBEAN_APPLICATION_KEY, applicationContext.getApplicationName()));
+ }
+ catch (Exception ex) {
+ throw new ApplicationContextException("Failed to unregister LiveBeansView MBean", ex);
+ }
+ }
+ }
+ }
+
+
+ private ConfigurableApplicationContext applicationContext;
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext,
+ "ApplicationContext does not implement ConfigurableApplicationContext");
+ this.applicationContext = (ConfigurableApplicationContext) applicationContext;
+ }
+
+
+ /**
+ * Generate a JSON snapshot of current beans and their dependencies,
+ * finding all active ApplicationContexts through {@link #findApplicationContexts()},
+ * then delegating to {@link #generateJson(java.util.Set)}.
+ */
+ public String getSnapshotAsJson() {
+ Set contexts;
+ if (this.applicationContext != null) {
+ contexts = Collections.singleton(this.applicationContext);
+ }
+ else {
+ contexts = findApplicationContexts();
+ }
+ return generateJson(contexts);
+ }
+
+ /**
+ * Actually generate a JSON snapshot of the beans in the given ApplicationContexts
+ * @param contexts the set of ApplicationContexts
+ * @return the JSON document
+ */
+ protected String generateJson(Set contexts) {
+ StringBuilder result = new StringBuilder();
+ for (ConfigurableApplicationContext context : contexts) {
+ result.append("{\n\"context\": \"").append(context.getId()).append("\"\n");
+ if (context.getParent() != null) {
+ result.append("\"parent\": \"").append(context.getParent().getId()).append("\"\n");
+ }
+ else {
+ result.append("\"parent\": null\n");
+ }
+ ConfigurableListableBeanFactory bf = context.getBeanFactory();
+ String[] beanNames = bf.getBeanDefinitionNames();
+ for (String beanName : beanNames) {
+ BeanDefinition bd = bf.getBeanDefinition(beanName);
+ if (bd.getRole() != BeanDefinition.ROLE_INFRASTRUCTURE &&
+ (!bd.isLazyInit() || bf.containsSingleton(beanName))) {
+ result.append("{\n\"bean\": \"").append(beanName).append("\"\n");
+ String scope = bd.getScope();
+ if (!StringUtils.hasText(scope)) {
+ scope = BeanDefinition.SCOPE_SINGLETON;
+ }
+ result.append("\"scope\": \"").append(scope).append("\"\n");
+ Class beanType = bf.getType(beanName);
+ if (beanType != null) {
+ result.append("\"type\": \"").append(beanType.getName()).append("\"\n");
+ }
+ else {
+ result.append("\"type\": null\n");
+ }
+ result.append("\"resource\": \"").append(bd.getResourceDescription()).append("\"\n");
+ result.append("\"dependencies\": [");
+ String[] dependencies = bf.getDependenciesForBean(beanName);
+ if (dependencies.length > 0) {
+ result.append("\"");
+ }
+ result.append(StringUtils.arrayToDelimitedString(dependencies, "\", \""));
+ if (dependencies.length > 0) {
+ result.append("\"");
+ }
+ result.append("]\n}\n");
+ }
+ }
+ result.append("}");
+ }
+ return result.toString();
+ }
+
+ /**
+ * Find all applicable ApplicationContexts for the current application.
+ * Called if no specific ApplicationContext has been set for this LiveBeansView.
+ * @return the set of ApplicationContexts
+ */
+ protected Set findApplicationContexts() {
+ synchronized (applicationContexts) {
+ return new LinkedHashSet(applicationContexts);
+ }
+ }
+
+}
diff --git a/spring-context/src/main/java/org/springframework/context/support/LiveBeansViewMBean.java b/spring-context/src/main/java/org/springframework/context/support/LiveBeansViewMBean.java
new file mode 100644
index 0000000000..4065a557d0
--- /dev/null
+++ b/spring-context/src/main/java/org/springframework/context/support/LiveBeansViewMBean.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed 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 org.springframework.context.support;
+
+/**
+ * MBean operation interface for the {@link LiveBeansView} feature.
+ *
+ * @author Juergen Hoeller
+ * @since 3.2
+ */
+public interface LiveBeansViewMBean {
+
+ /**
+ * Generate a JSON snapshot of current beans and their dependencies.
+ */
+ String getSnapshotAsJson();
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java b/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java
index de5e078ec7..7f099ac849 100644
--- a/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java
+++ b/spring-web/src/main/java/org/springframework/web/context/support/AbstractRefreshableWebApplicationContext.java
@@ -109,7 +109,7 @@ public abstract class AbstractRefreshableWebApplicationContext extends AbstractR
public void setServletConfig(ServletConfig servletConfig) {
this.servletConfig = servletConfig;
if (servletConfig != null && this.servletContext == null) {
- this.setServletContext(servletConfig.getServletContext());
+ setServletContext(servletConfig.getServletContext());
}
}
@@ -133,6 +133,21 @@ public abstract class AbstractRefreshableWebApplicationContext extends AbstractR
return super.getConfigLocations();
}
+ @Override
+ public String getApplicationName() {
+ if (this.servletContext == null) {
+ return "";
+ }
+ if (this.servletContext.getMajorVersion() == 2 && this.servletContext.getMinorVersion() < 5) {
+ String name = this.servletContext.getServletContextName();
+ return (name != null ? name : "");
+ }
+ else {
+ // Servlet 2.5 available
+ return this.servletContext.getContextPath();
+ }
+ }
+
/**
* Create and return a new {@link StandardServletEnvironment}. Subclasses may override
* in order to configure the environment or specialize the environment type returned.
diff --git a/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java b/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java
index 99f83dcc5d..2ddb0466a0 100644
--- a/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java
+++ b/spring-web/src/main/java/org/springframework/web/context/support/GenericWebApplicationContext.java
@@ -68,6 +68,7 @@ public class GenericWebApplicationContext extends GenericApplicationContext
private ThemeSource themeSource;
+
/**
* Create a new GenericWebApplicationContext.
* @see #setServletContext
@@ -123,6 +124,20 @@ public class GenericWebApplicationContext extends GenericApplicationContext
return this.servletContext;
}
+ @Override
+ public String getApplicationName() {
+ if (this.servletContext == null) {
+ return "";
+ }
+ if (this.servletContext.getMajorVersion() == 2 && this.servletContext.getMinorVersion() < 5) {
+ String name = this.servletContext.getServletContextName();
+ return (name != null ? name : "");
+ }
+ else {
+ // Servlet 2.5 available
+ return this.servletContext.getContextPath();
+ }
+ }
/**
* Create and return a new {@link StandardServletEnvironment}.
diff --git a/spring-web/src/main/java/org/springframework/web/context/support/LiveBeansViewServlet.java b/spring-web/src/main/java/org/springframework/web/context/support/LiveBeansViewServlet.java
new file mode 100644
index 0000000000..453926f541
--- /dev/null
+++ b/spring-web/src/main/java/org/springframework/web/context/support/LiveBeansViewServlet.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed 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 org.springframework.web.context.support;
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.context.support.LiveBeansView;
+
+/**
+ * Servlet variant of {@link LiveBeansView}'s MBean exposure.
+ *
+ * Generates a JSON snapshot for current beans and their dependencies in
+ * all ApplicationContexts that live within the current web application.
+ *
+ * @author Juergen Hoeller
+ * @since 3.2
+ * @see org.springframework.context.support.LiveBeansView#getSnapshotAsJson()
+ */
+public class LiveBeansViewServlet extends HttpServlet {
+
+ private LiveBeansView liveBeansView;
+
+ @Override
+ public void init() throws ServletException {
+ this.liveBeansView = buildLiveBeansView();
+ }
+
+ protected LiveBeansView buildLiveBeansView() {
+ return new ServletContextLiveBeansView(getServletContext());
+ }
+
+ @Override
+ protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+ String content = this.liveBeansView.getSnapshotAsJson();
+ response.setContentType("application/json");
+ response.setContentLength(content.length());
+ response.getWriter().write(content);
+ }
+
+}
diff --git a/spring-web/src/main/java/org/springframework/web/context/support/ServletContextLiveBeansView.java b/spring-web/src/main/java/org/springframework/web/context/support/ServletContextLiveBeansView.java
new file mode 100644
index 0000000000..df7a4537b0
--- /dev/null
+++ b/spring-web/src/main/java/org/springframework/web/context/support/ServletContextLiveBeansView.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2002-2012 the original author or authors.
+ *
+ * Licensed 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 org.springframework.web.context.support;
+
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.Set;
+import javax.servlet.ServletContext;
+
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.support.LiveBeansView;
+import org.springframework.util.Assert;
+
+/**
+ * {@link LiveBeansView} subclass which looks for all ApplicationContexts
+ * in the web application, as exposed in ServletContext attributes.
+ *
+ * @author Juergen Hoeller
+ * @since 3.2
+ */
+public class ServletContextLiveBeansView extends LiveBeansView {
+
+ private final ServletContext servletContext;
+
+ /**
+ * Create a new LiveBeansView for the given ServletContext.
+ * @param servletContext current ServletContext
+ */
+ public ServletContextLiveBeansView(ServletContext servletContext) {
+ Assert.notNull(servletContext, "ServletContext must not be null");
+ this.servletContext = servletContext;
+ }
+
+ @Override
+ protected Set findApplicationContexts() {
+ Set contexts = new LinkedHashSet();
+ Enumeration attrNames = this.servletContext.getAttributeNames();
+ while (attrNames.hasMoreElements()) {
+ String attrName = attrNames.nextElement();
+ Object attrValue = this.servletContext.getAttribute(attrName);
+ if (attrValue instanceof ConfigurableApplicationContext) {
+ contexts.add((ConfigurableApplicationContext) attrValue);
+ }
+ }
+ return contexts;
+ }
+
+}
diff --git a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/AbstractRefreshablePortletApplicationContext.java b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/AbstractRefreshablePortletApplicationContext.java
index 56b04da88f..c61dd503ed 100644
--- a/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/AbstractRefreshablePortletApplicationContext.java
+++ b/spring-webmvc-portlet/src/main/java/org/springframework/web/portlet/context/AbstractRefreshablePortletApplicationContext.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2009 the original author or authors.
+ * Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -143,6 +143,22 @@ public abstract class AbstractRefreshablePortletApplicationContext extends Abstr
return super.getConfigLocations();
}
+ @Override
+ public String getApplicationName() {
+ if (this.portletContext == null) {
+ return "";
+ }
+ String name = this.portletContext.getPortletContextName();
+ return (name != null ? name : "");
+ }
+
+ /**
+ * Create and return a new {@link StandardPortletEnvironment}.
+ */
+ @Override
+ protected ConfigurableEnvironment createEnvironment() {
+ return new StandardPortletEnvironment();
+ }
/**
* Register request/session scopes, a {@link PortletContextAwareProcessor}, etc.
@@ -160,14 +176,6 @@ public abstract class AbstractRefreshablePortletApplicationContext extends Abstr
beanFactory, this.servletContext, this.portletContext, this.portletConfig);
}
- /**
- * Create and return a new {@link StandardPortletEnvironment}.
- */
- @Override
- protected ConfigurableEnvironment createEnvironment() {
- return new StandardPortletEnvironment();
- }
-
/**
* This implementation supports file paths beneath the root of the PortletContext.
* @see PortletContextResource
@@ -190,4 +198,5 @@ public abstract class AbstractRefreshablePortletApplicationContext extends Abstr
protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
super.customizeBeanFactory(beanFactory);
}
+
}