From 889194db63016ad4b9ecb0c6ae82d3d9c7632c95 Mon Sep 17 00:00:00 2001
From: Justin Clark-Casey (justincc)
Date: Wed, 2 Jul 2014 23:48:44 +0100
Subject: Actually call Close() for shared region modules when the simulator is
being shutdown.
Adds regression test for this case.
---
.../RegionModulesControllerPlugin.cs | 87 ++++---
OpenSim/Framework/Servers/BaseOpenSimServer.cs | 8 +-
OpenSim/Framework/Util.cs | 7 +-
OpenSim/Region/Application/OpenSimBase.cs | 44 +++-
.../Region/CoreModules/Asset/CenomeAssetCache.cs | 9 +-
OpenSim/Region/Framework/Scenes/Scene.cs | 1 +
.../Scenes/Tests/SharedRegionModuleTests.cs | 249 +++++++++++++++++++++
OpenSim/Tests/Common/OpenSimTestCase.cs | 1 +
prebuild.xml | 4 +
9 files changed, 361 insertions(+), 49 deletions(-)
create mode 100644 OpenSim/Region/Framework/Scenes/Tests/SharedRegionModuleTests.cs
diff --git a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs
index 510be37..e03483a 100644
--- a/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs
+++ b/OpenSim/ApplicationPlugins/RegionModulesController/RegionModulesControllerPlugin.cs
@@ -32,6 +32,7 @@ using log4net;
using Mono.Addins;
using Nini.Config;
using OpenSim;
+using OpenSim.Framework;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
@@ -45,6 +46,12 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
LogManager.GetLogger(
MethodBase.GetCurrentMethod().DeclaringType);
+ ///
+ /// Controls whether we load modules from Mono.Addins.
+ ///
+ /// For debug purposes. Defaults to true.
+ public bool LoadModulesFromAddins { get; set; }
+
// Config access
private OpenSimBase m_openSim;
@@ -61,6 +68,11 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
private List m_sharedInstances =
new List();
+ public RegionModulesControllerPlugin()
+ {
+ LoadModulesFromAddins = true;
+ }
+
#region IApplicationPlugin implementation
public void Initialise (OpenSimBase openSim)
@@ -69,6 +81,9 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
m_openSim.ApplicationRegistry.RegisterInterface(this);
m_log.DebugFormat("[REGIONMODULES]: Initializing...");
+ if (!LoadModulesFromAddins)
+ return;
+
// Who we are
string id = AddinManager.CurrentAddin.Id;
@@ -88,40 +103,8 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
Dictionary> loadedModules = new Dictionary>();
// Scan modules and load all that aren't disabled
- foreach (TypeExtensionNode node in
- AddinManager.GetExtensionNodes("/OpenSim/RegionModules"))
- {
- IList loadedModuleData;
-
- if (!loadedModules.ContainsKey(node.Addin))
- loadedModules.Add(node.Addin, new List { 0, 0, 0 });
-
- loadedModuleData = loadedModules[node.Addin];
-
- if (node.Type.GetInterface(typeof(ISharedRegionModule).ToString()) != null)
- {
- if (CheckModuleEnabled(node, modulesConfig))
- {
- m_log.DebugFormat("[REGIONMODULES]: Found shared region module {0}, class {1}", node.Id, node.Type);
- m_sharedModules.Add(node);
- loadedModuleData[0]++;
- }
- }
- else if (node.Type.GetInterface(typeof(INonSharedRegionModule).ToString()) != null)
- {
- if (CheckModuleEnabled(node, modulesConfig))
- {
- m_log.DebugFormat("[REGIONMODULES]: Found non-shared region module {0}, class {1}", node.Id, node.Type);
- m_nonSharedModules.Add(node);
- loadedModuleData[1]++;
- }
- }
- else
- {
- m_log.WarnFormat("[REGIONMODULES]: Found unknown type of module {0}, class {1}", node.Id, node.Type);
- loadedModuleData[2]++;
- }
- }
+ foreach (TypeExtensionNode node in AddinManager.GetExtensionNodes("/OpenSim/RegionModules"))
+ AddNode(node, modulesConfig, loadedModules);
foreach (KeyValuePair> loadedModuleData in loadedModules)
{
@@ -194,6 +177,41 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
#region IPlugin implementation
+ private void AddNode(
+ TypeExtensionNode node, IConfig modulesConfig, Dictionary> loadedModules)
+ {
+ IList loadedModuleData;
+
+ if (!loadedModules.ContainsKey(node.Addin))
+ loadedModules.Add(node.Addin, new List { 0, 0, 0 });
+
+ loadedModuleData = loadedModules[node.Addin];
+
+ if (node.Type.GetInterface(typeof(ISharedRegionModule).ToString()) != null)
+ {
+ if (CheckModuleEnabled(node, modulesConfig))
+ {
+ m_log.DebugFormat("[REGIONMODULES]: Found shared region module {0}, class {1}", node.Id, node.Type);
+ m_sharedModules.Add(node);
+ loadedModuleData[0]++;
+ }
+ }
+ else if (node.Type.GetInterface(typeof(INonSharedRegionModule).ToString()) != null)
+ {
+ if (CheckModuleEnabled(node, modulesConfig))
+ {
+ m_log.DebugFormat("[REGIONMODULES]: Found non-shared region module {0}, class {1}", node.Id, node.Type);
+ m_nonSharedModules.Add(node);
+ loadedModuleData[1]++;
+ }
+ }
+ else
+ {
+ m_log.WarnFormat("[REGIONMODULES]: Found unknown type of module {0}, class {1}", node.Id, node.Type);
+ loadedModuleData[2]++;
+ }
+ }
+
// We don't do that here
//
public void Initialise ()
@@ -215,6 +233,7 @@ namespace OpenSim.ApplicationPlugins.RegionModulesController
m_sharedInstances[0].Close();
m_sharedInstances.RemoveAt(0);
}
+
m_sharedModules.Clear();
m_nonSharedModules.Clear();
}
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
index 54e6061..828a852 100644
--- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs
+++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
@@ -57,6 +57,11 @@ namespace OpenSim.Framework.Servers
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
///
+ /// Used by tests to suppress Environment.Exit(0) so that post-run operations are possible.
+ ///
+ public bool SuppressExit { get; set; }
+
+ ///
/// This will control a periodic log printout of the current 'show stats' (if they are active) for this
/// server.
///
@@ -109,7 +114,8 @@ namespace OpenSim.Framework.Servers
base.ShutdownSpecific();
- Environment.Exit(0);
+ if (!SuppressExit)
+ Environment.Exit(0);
}
///
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index 740e55a..729281c 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -1964,10 +1964,15 @@ namespace OpenSim.Framework
{
if (maxThreads < 2)
throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2");
+
if (minThreads > maxThreads || minThreads < 2)
throw new ArgumentOutOfRangeException("minThreads", "minThreads must be greater than 2 and less than or equal to maxThreads");
+
if (m_ThreadPool != null)
- throw new InvalidOperationException("SmartThreadPool is already initialized");
+ {
+ m_log.Warn("SmartThreadPool is already initialized. Ignoring request.");
+ return;
+ }
STPStartInfo startInfo = new STPStartInfo();
startInfo.ThreadPoolName = "Util";
diff --git a/OpenSim/Region/Application/OpenSimBase.cs b/OpenSim/Region/Application/OpenSimBase.cs
index 4c1914a..d2dce24 100644
--- a/OpenSim/Region/Application/OpenSimBase.cs
+++ b/OpenSim/Region/Application/OpenSimBase.cs
@@ -71,6 +71,20 @@ namespace OpenSim
// OpenSim.ini Section name for ESTATES Settings
public const string ESTATE_SECTION_NAME = "Estates";
+ ///
+ /// Allow all plugin loading to be disabled for tests/debug.
+ ///
+ ///
+ /// true by default
+ ///
+ public bool EnableInitialPluginLoad { get; set; }
+
+ ///
+ /// Control whether we attempt to load an estate data service.
+ ///
+ /// For tests/debugging
+ public bool LoadEstateDataService { get; set; }
+
protected string proxyUrl;
protected int proxyOffset = 0;
@@ -96,7 +110,7 @@ namespace OpenSim
public ConsoleCommand CreateAccount = null;
- protected List m_plugins = new List();
+ public List m_plugins = new List();
///
/// The config information passed into the OpenSimulator region server.
@@ -135,6 +149,8 @@ namespace OpenSim
///
public OpenSimBase(IConfigSource configSource) : base()
{
+ EnableInitialPluginLoad = true;
+ LoadEstateDataService = true;
LoadConfigSettings(configSource);
}
@@ -236,20 +252,25 @@ namespace OpenSim
if (String.IsNullOrEmpty(module))
throw new Exception("Configuration file is missing the LocalServiceModule parameter in the [EstateDataStore] or [EstateService] section");
- m_estateDataService = ServerUtils.LoadPlugin(module, new object[] { Config });
- if (m_estateDataService == null)
- throw new Exception(
- string.Format(
- "Could not load an IEstateDataService implementation from {0}, as configured in the LocalServiceModule parameter of the [EstateDataStore] config section.",
- module));
+ if (LoadEstateDataService)
+ {
+ m_estateDataService = ServerUtils.LoadPlugin(module, new object[] { Config });
+ if (m_estateDataService == null)
+ throw new Exception(
+ string.Format(
+ "Could not load an IEstateDataService implementation from {0}, as configured in the LocalServiceModule parameter of the [EstateDataStore] config section.",
+ module));
+ }
base.StartupSpecific();
- LoadPlugins();
+ if (EnableInitialPluginLoad)
+ LoadPlugins();
+
+ // We still want to post initalize any plugins even if loading has been disabled since a test may have
+ // inserted them manually.
foreach (IApplicationPlugin plugin in m_plugins)
- {
plugin.PostInitialise();
- }
if (m_console != null)
AddPluginCommands(m_console);
@@ -874,6 +895,9 @@ namespace OpenSim
try
{
SceneManager.Close();
+
+ foreach (IApplicationPlugin plugin in m_plugins)
+ plugin.Dispose();
}
catch (Exception e)
{
diff --git a/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs b/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs
index 9b0e1f4..ebec9d2 100644
--- a/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs
+++ b/OpenSim/Region/CoreModules/Asset/CenomeAssetCache.cs
@@ -316,9 +316,12 @@ namespace OpenSim.Region.CoreModules.Asset
///
public void Close()
{
- m_enabled = false;
- m_cache.Clear();
- m_cache = null;
+ if (m_enabled)
+ {
+ m_enabled = false;
+ m_cache.Clear();
+ m_cache = null;
+ }
}
///
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index ca42d5b..3957ba6 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -1894,6 +1894,7 @@ namespace OpenSim.Region.Framework.Scenes
RegionInfo.RegionID,
RegionInfo.RegionLocX, RegionInfo.RegionLocY,
RegionInfo.RegionSizeX, RegionInfo.RegionSizeY);
+
if (error != String.Empty)
throw new Exception(error);
}
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SharedRegionModuleTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SharedRegionModuleTests.cs
new file mode 100644
index 0000000..d499f87
--- /dev/null
+++ b/OpenSim/Region/Framework/Scenes/Tests/SharedRegionModuleTests.cs
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the OpenSimulator Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Collections.Generic;
+using System.Net;
+using Mono.Addins;
+using Nini.Config;
+using NUnit.Framework;
+using OpenMetaverse;
+using OpenSim;
+using OpenSim.ApplicationPlugins.RegionModulesController;
+using OpenSim.Framework;
+using OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Tests.Common;
+
+namespace OpenSim.Region.Framework.Scenes.Tests
+{
+ public class SharedRegionModuleTests : OpenSimTestCase
+ {
+ [Test]
+ public void TestLifecycle()
+ {
+ TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
+
+ UUID estateOwnerId = TestHelpers.ParseTail(0x1);
+ UUID regionId = TestHelpers.ParseTail(0x10);
+
+ IConfigSource configSource = new IniConfigSource();
+ configSource.AddConfig("Startup");
+ configSource.AddConfig("Modules");
+
+// // We use this to skip estate questions
+ // Turns out not to be needed is estate owner id is pre-set in region information.
+// IConfig estateConfig = configSource.AddConfig(OpenSimBase.ESTATE_SECTION_NAME);
+// estateConfig.Set("DefaultEstateOwnerName", "Zaphod Beeblebrox");
+// estateConfig.Set("DefaultEstateOwnerUUID", estateOwnerId);
+// estateConfig.Set("DefaultEstateOwnerEMail", "zaphod@galaxy.com");
+// estateConfig.Set("DefaultEstateOwnerPassword", "two heads");
+
+ // For grid servic
+ configSource.AddConfig("GridService");
+ configSource.Configs["Modules"].Set("GridServices", "LocalGridServicesConnector");
+ configSource.Configs["GridService"].Set("StorageProvider", "OpenSim.Data.Null.dll:NullRegionData");
+ configSource.Configs["GridService"].Set("LocalServiceModule", "OpenSim.Services.GridService.dll:GridService");
+ configSource.Configs["GridService"].Set("ConnectionString", "!static");
+
+ LocalGridServicesConnector gridService = new LocalGridServicesConnector();
+//
+ OpenSim sim = new OpenSim(configSource);
+
+ sim.SuppressExit = true;
+ sim.EnableInitialPluginLoad = false;
+ sim.LoadEstateDataService = false;
+ sim.NetServersInfo.HttpListenerPort = 0;
+
+ IRegistryCore reg = sim.ApplicationRegistry;
+
+ RegionInfo ri = new RegionInfo();
+ ri.RegionID = regionId;
+ ri.EstateSettings.EstateOwner = estateOwnerId;
+ ri.InternalEndPoint = new IPEndPoint(0, 0);
+
+ MockRegionModulesControllerPlugin rmcp = new MockRegionModulesControllerPlugin();
+ sim.m_plugins = new List() { rmcp };
+ reg.RegisterInterface(rmcp);
+
+ // XXX: Have to initialize directly for now
+ rmcp.Initialise(sim);
+
+ rmcp.AddNode(gridService);
+
+ TestSharedRegion tsr = new TestSharedRegion();
+ rmcp.AddNode(tsr);
+
+ // FIXME: Want to use the real one eventually but this is currently directly tied into Mono.Addins
+ // which has been written in such a way that makes it impossible to use for regression tests.
+// RegionModulesControllerPlugin rmcp = new RegionModulesControllerPlugin();
+// rmcp.LoadModulesFromAddins = false;
+//// reg.RegisterInterface(rmcp);
+// rmcp.Initialise(sim);
+// rmcp.PostInitialise();
+// TypeExtensionNode node = new TypeExtensionNode();
+// node.
+// rmcp.AddNode(node, configSource.Configs["Modules"], new Dictionary>());
+
+ sim.Startup();
+ IScene scene;
+ sim.CreateRegion(ri, out scene);
+
+ sim.Shutdown();
+
+ List co = tsr.CallOrder;
+ int expectedEventCount = 6;
+
+ Assert.AreEqual(
+ expectedEventCount,
+ co.Count,
+ "Expected {0} events but only got {1} ({2})",
+ expectedEventCount, co.Count, string.Join(",", co));
+ Assert.AreEqual("Initialise", co[0]);
+ Assert.AreEqual("PostInitialise", co[1]);
+ Assert.AreEqual("AddRegion", co[2]);
+ Assert.AreEqual("RegionLoaded", co[3]);
+ Assert.AreEqual("RemoveRegion", co[4]);
+ Assert.AreEqual("Close", co[5]);
+ }
+ }
+
+ class TestSharedRegion : ISharedRegionModule
+ {
+ // FIXME: Should really use MethodInfo
+ public List CallOrder = new List();
+
+ public string Name { get { return "TestSharedRegion"; } }
+
+ public Type ReplaceableInterface { get { return null; } }
+
+ public void PostInitialise()
+ {
+ CallOrder.Add("PostInitialise");
+ }
+
+ public void Initialise(IConfigSource source)
+ {
+ CallOrder.Add("Initialise");
+ }
+
+ public void Close()
+ {
+ CallOrder.Add("Close");
+ }
+
+ public void AddRegion(Scene scene)
+ {
+ CallOrder.Add("AddRegion");
+ }
+
+ public void RemoveRegion(Scene scene)
+ {
+ CallOrder.Add("RemoveRegion");
+ }
+
+ public void RegionLoaded(Scene scene)
+ {
+ CallOrder.Add("RegionLoaded");
+ }
+ }
+
+ class MockRegionModulesControllerPlugin : IRegionModulesController, IApplicationPlugin
+ {
+ // List of shared module instances, for adding to Scenes
+ private List m_sharedInstances = new List();
+
+ // Config access
+ private OpenSimBase m_openSim;
+
+ public string Version { get { return "0"; } }
+ public string Name { get { return "MockRegionModulesControllerPlugin"; } }
+
+ public void Initialise() {}
+
+ public void Initialise(OpenSimBase sim)
+ {
+ m_openSim = sim;
+ }
+
+ ///
+ /// Called when the application loading is completed
+ ///
+ public void PostInitialise()
+ {
+ foreach (ISharedRegionModule module in m_sharedInstances)
+ module.PostInitialise();
+ }
+
+ public void AddRegionToModules(Scene scene)
+ {
+ List sharedlist = new List();
+
+ foreach (ISharedRegionModule module in m_sharedInstances)
+ {
+ module.AddRegion(scene);
+ scene.AddRegionModule(module.Name, module);
+
+ sharedlist.Add(module);
+ }
+
+ foreach (ISharedRegionModule module in sharedlist)
+ {
+ module.RegionLoaded(scene);
+ }
+ }
+
+ public void RemoveRegionFromModules(Scene scene)
+ {
+ foreach (IRegionModuleBase module in scene.RegionModules.Values)
+ {
+// m_log.DebugFormat("[REGIONMODULE]: Removing scene {0} from module {1}",
+// scene.RegionInfo.RegionName, module.Name);
+ module.RemoveRegion(scene);
+ }
+
+ scene.RegionModules.Clear();
+ }
+
+ public void AddNode(ISharedRegionModule module)
+ {
+ m_sharedInstances.Add(module);
+ module.Initialise(m_openSim.ConfigSource.Source);
+ }
+
+ public void Dispose()
+ {
+ // We expect that all regions have been removed already
+ while (m_sharedInstances.Count > 0)
+ {
+ m_sharedInstances[0].Close();
+ m_sharedInstances.RemoveAt(0);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Tests/Common/OpenSimTestCase.cs b/OpenSim/Tests/Common/OpenSimTestCase.cs
index 3c47faa..c1415af 100644
--- a/OpenSim/Tests/Common/OpenSimTestCase.cs
+++ b/OpenSim/Tests/Common/OpenSimTestCase.cs
@@ -27,6 +27,7 @@
using System;
using NUnit.Framework;
+using OpenSim.Framework;
using OpenSim.Framework.Servers;
namespace OpenSim.Tests.Common
diff --git a/prebuild.xml b/prebuild.xml
index 0143e26..e2423ae 100644
--- a/prebuild.xml
+++ b/prebuild.xml
@@ -3185,8 +3185,11 @@
+
+
+
@@ -3195,6 +3198,7 @@
+
--
cgit v1.1