OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
ServerUtils.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.IO;
30 using System.Reflection;
31 using System.Xml;
32 using System.Xml.Serialization;
33 using System.Text;
34 using System.Collections.Generic;
35 using log4net;
36 using Nini.Config;
37 using OpenSim.Framework;
38 using OpenMetaverse;
39 using Mono.Addins;
40 using OpenSim.Framework.Servers.HttpServer;
41 using OpenSim.Framework.Servers;
42 
43 
44 [assembly:AddinRoot("Robust", OpenSim.VersionInfo.VersionNumber)]
45 namespace OpenSim.Server.Base
46 {
47  [TypeExtensionPoint(Path="/Robust/Connector", Name="RobustConnector")]
48  public interface IRobustConnector
49  {
50  string ConfigName
51  {
52  get;
53  }
54 
55  bool Enabled
56  {
57  get;
58  }
59 
60  string PluginPath
61  {
62  get;
63  set;
64  }
65 
66  uint Configure(IConfigSource config);
67  void Initialize(IHttpServer server);
68  void Unload();
69  }
70 
71  public class PluginLoader
72  {
73  static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
74 
75  public AddinRegistry Registry
76  {
77  get;
78  private set;
79  }
80 
81  public IConfigSource Config
82  {
83  get;
84  private set;
85  }
86 
87  public PluginLoader(IConfigSource config, string registryPath)
88  {
89  Config = config;
90 
91  Registry = new AddinRegistry(registryPath, ".");
92  //suppress_console_output_(true);
93  AddinManager.Initialize(registryPath);
94  //suppress_console_output_(false);
95  AddinManager.Registry.Update();
96  CommandManager commandmanager = new CommandManager(Registry);
97  AddinManager.AddExtensionNodeHandler("/Robust/Connector", OnExtensionChanged);
98  }
99 
100  private static TextWriter prev_console_;
101  // Temporarily masking the errors reported on start
102  // This is caused by a non-managed dll in the ./bin dir
103  // when the registry is initialized. The dll belongs to
104  // libomv, which has a hard-coded path to "." for pinvoke
105  // to load the openjpeg dll
106  //
107  // Will look for a way to fix, but for now this keeps the
108  // confusion to a minimum. this was copied from our region
109  // plugin loader, we have been doing this in there for a long time.
110  //
111  public void suppress_console_output_(bool save)
112  {
113  if (save)
114  {
115  prev_console_ = System.Console.Out;
116  System.Console.SetOut(new StreamWriter(Stream.Null));
117  }
118  else
119  {
120  if (prev_console_ != null)
121  System.Console.SetOut(prev_console_);
122  }
123  }
124 
125  private void OnExtensionChanged(object s, ExtensionNodeEventArgs args)
126  {
127  IRobustConnector connector = (IRobustConnector)args.ExtensionObject;
128  Addin a = Registry.GetAddin(args.ExtensionNode.Addin.Id);
129 
130  if(a == null)
131  {
132  Registry.Rebuild(null);
133  a = Registry.GetAddin(args.ExtensionNode.Addin.Id);
134  }
135 
136  switch(args.Change)
137  {
138  case ExtensionChange.Add:
139  if (a.AddinFile.Contains(Registry.DefaultAddinsFolder))
140  {
141  m_log.InfoFormat("[SERVER UTILS]: Adding {0} from registry", a.Name);
142  connector.PluginPath = System.IO.Path.Combine(Registry.DefaultAddinsFolder,a.Name.Replace(',', '.')); }
143  else
144  {
145  m_log.InfoFormat("[SERVER UTILS]: Adding {0} from ./bin", a.Name);
146  connector.PluginPath = a.AddinFile;
147  }
148  LoadPlugin(connector);
149  break;
150  case ExtensionChange.Remove:
151  m_log.InfoFormat("[SERVER UTILS]: Removing {0}", a.Name);
152  UnloadPlugin(connector);
153  break;
154  }
155  }
156 
157  private void LoadPlugin(IRobustConnector connector)
158  {
159  IHttpServer server = null;
160  uint port = connector.Configure(Config);
161 
162  if(connector.Enabled)
163  {
164  server = GetServer(connector, port);
165  connector.Initialize(server);
166  }
167  else
168  {
169  m_log.InfoFormat("[SERVER UTILS]: {0} Disabled.", connector.ConfigName);
170  }
171  }
172 
173  private void UnloadPlugin(IRobustConnector connector)
174  {
175  m_log.InfoFormat("[SERVER UTILS]: Unloading {0}", connector.ConfigName);
176 
177  connector.Unload();
178  }
179 
180  private IHttpServer GetServer(IRobustConnector connector, uint port)
181  {
182  IHttpServer server;
183 
184  if(port != 0)
185  server = MainServer.GetHttpServer(port);
186  else
187  server = MainServer.Instance;
188 
189  return server;
190  }
191  }
192 
193  public static class ServerUtils
194  {
195  private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
196 
197  public static byte[] SerializeResult(XmlSerializer xs, object data)
198  {
199  using (MemoryStream ms = new MemoryStream())
200  using (XmlTextWriter xw = new XmlTextWriter(ms, Util.UTF8))
201  {
202  xw.Formatting = Formatting.Indented;
203  xs.Serialize(xw, data);
204  xw.Flush();
205 
206  ms.Seek(0, SeekOrigin.Begin);
207  byte[] ret = ms.GetBuffer();
208  Array.Resize(ref ret, (int)ms.Length);
209 
210  return ret;
211  }
212  }
213 
220  public static T LoadPlugin<T> (string dllName, Object[] args) where T:class
221  {
222  // This is good to debug configuration problems
223  //if (dllName == string.Empty)
224  // Util.PrintCallStack();
225 
226  string className = String.Empty;
227 
228  // The path for a dynamic plugin will contain ":" on Windows
229  string[] parts = dllName.Split (new char[] {':'});
230 
231  if (parts [0].Length > 1)
232  {
233  dllName = parts [0];
234  if (parts.Length > 1)
235  className = parts[1];
236  }
237  else
238  {
239  // This is Windows - we must replace the ":" in the path
240  dllName = String.Format ("{0}:{1}", parts [0], parts [1]);
241  if (parts.Length > 2)
242  className = parts[2];
243  }
244 
245  return LoadPlugin<T>(dllName, className, args);
246  }
247 
255  public static T LoadPlugin<T>(string dllName, string className, Object[] args) where T:class
256  {
257  string interfaceName = typeof(T).ToString();
258 
259  try
260  {
261  Assembly pluginAssembly = Assembly.LoadFrom(dllName);
262 
263  foreach (Type pluginType in pluginAssembly.GetTypes())
264  {
265  if (pluginType.IsPublic)
266  {
267  if (className != String.Empty
268  && pluginType.ToString() != pluginType.Namespace + "." + className)
269  continue;
270 
271  Type typeInterface = pluginType.GetInterface(interfaceName);
272 
273  if (typeInterface != null)
274  {
275  T plug = null;
276  try
277  {
278  plug = (T)Activator.CreateInstance(pluginType,
279  args);
280  }
281  catch (Exception e)
282  {
283  if (!(e is System.MissingMethodException))
284  {
285  m_log.Error(string.Format("[SERVER UTILS]: Error loading plugin {0} from {1}. Exception: {2}",
286  interfaceName,
287  dllName,
288  e.InnerException == null ? e.Message : e.InnerException.Message),
289  e);
290  }
291  m_log.ErrorFormat("[SERVER UTILS]: Error loading plugin {0}: {1} args.Length {2}", dllName, e.Message, args.Length);
292  return null;
293  }
294 
295  return plug;
296  }
297  }
298  }
299 
300  return null;
301  }
302  catch (ReflectionTypeLoadException rtle)
303  {
304  m_log.Error(string.Format("[SERVER UTILS]: Error loading plugin from {0}:\n{1}", dllName,
305  String.Join("\n", Array.ConvertAll(rtle.LoaderExceptions, e => e.ToString()))),
306  rtle);
307  return null;
308  }
309  catch (Exception e)
310  {
311  m_log.Error(string.Format("[SERVER UTILS]: Error loading plugin from {0}", dllName), e);
312  return null;
313  }
314  }
315 
316  public static Dictionary<string, object> ParseQueryString(string query)
317  {
318  Dictionary<string, object> result = new Dictionary<string, object>();
319  string[] terms = query.Split(new char[] {'&'});
320 
321  if (terms.Length == 0)
322  return result;
323 
324  foreach (string t in terms)
325  {
326  string[] elems = t.Split(new char[] {'='});
327  if (elems.Length == 0)
328  continue;
329 
330  string name = System.Web.HttpUtility.UrlDecode(elems[0]);
331  string value = String.Empty;
332 
333  if (elems.Length > 1)
334  value = System.Web.HttpUtility.UrlDecode(elems[1]);
335 
336  if (name.EndsWith("[]"))
337  {
338  string cleanName = name.Substring(0, name.Length - 2);
339  if (result.ContainsKey(cleanName))
340  {
341  if (!(result[cleanName] is List<string>))
342  continue;
343 
344  List<string> l = (List<string>)result[cleanName];
345 
346  l.Add(value);
347  }
348  else
349  {
350  List<string> newList = new List<string>();
351 
352  newList.Add(value);
353 
354  result[cleanName] = newList;
355  }
356  }
357  else
358  {
359  if (!result.ContainsKey(name))
360  result[name] = value;
361  }
362  }
363 
364  return result;
365  }
366 
367  public static string BuildQueryString(Dictionary<string, object> data)
368  {
369  string qstring = String.Empty;
370 
371  string part;
372 
373  foreach (KeyValuePair<string, object> kvp in data)
374  {
375  if (kvp.Value is List<string>)
376  {
377  List<string> l = (List<String>)kvp.Value;
378 
379  foreach (string s in l)
380  {
381  part = System.Web.HttpUtility.UrlEncode(kvp.Key) +
382  "[]=" + System.Web.HttpUtility.UrlEncode(s);
383 
384  if (qstring != String.Empty)
385  qstring += "&";
386 
387  qstring += part;
388  }
389  }
390  else
391  {
392  if (kvp.Value.ToString() != String.Empty)
393  {
394  part = System.Web.HttpUtility.UrlEncode(kvp.Key) +
395  "=" + System.Web.HttpUtility.UrlEncode(kvp.Value.ToString());
396  }
397  else
398  {
399  part = System.Web.HttpUtility.UrlEncode(kvp.Key);
400  }
401 
402  if (qstring != String.Empty)
403  qstring += "&";
404 
405  qstring += part;
406  }
407  }
408 
409  return qstring;
410  }
411 
412  public static string BuildXmlResponse(Dictionary<string, object> data)
413  {
414  XmlDocument doc = new XmlDocument();
415 
416  XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration,
417  "", "");
418 
419  doc.AppendChild(xmlnode);
420 
421  XmlElement rootElement = doc.CreateElement("", "ServerResponse",
422  "");
423 
424  doc.AppendChild(rootElement);
425 
426  BuildXmlData(rootElement, data);
427 
428  return doc.InnerXml;
429  }
430 
431  private static void BuildXmlData(XmlElement parent, Dictionary<string, object> data)
432  {
433  foreach (KeyValuePair<string, object> kvp in data)
434  {
435  if (kvp.Value == null)
436  continue;
437 
438  XmlElement elem = parent.OwnerDocument.CreateElement("",
439  XmlConvert.EncodeLocalName(kvp.Key), "");
440 
441  if (kvp.Value is Dictionary<string, object>)
442  {
443  XmlAttribute type = parent.OwnerDocument.CreateAttribute("",
444  "type", "");
445  type.Value = "List";
446 
447  elem.Attributes.Append(type);
448 
449  BuildXmlData(elem, (Dictionary<string, object>)kvp.Value);
450  }
451  else
452  {
453  elem.AppendChild(parent.OwnerDocument.CreateTextNode(
454  kvp.Value.ToString()));
455  }
456 
457  parent.AppendChild(elem);
458  }
459  }
460 
461  public static Dictionary<string, object> ParseXmlResponse(string data)
462  {
463  //m_log.DebugFormat("[XXX]: received xml string: {0}", data);
464 
465  Dictionary<string, object> ret = new Dictionary<string, object>();
466 
467  XmlDocument doc = new XmlDocument();
468 
469  doc.LoadXml(data);
470 
471  XmlNodeList rootL = doc.GetElementsByTagName("ServerResponse");
472 
473  if (rootL.Count != 1)
474  return ret;
475 
476  XmlNode rootNode = rootL[0];
477 
478  ret = ParseElement(rootNode);
479 
480  return ret;
481  }
482 
483  private static Dictionary<string, object> ParseElement(XmlNode element)
484  {
485  Dictionary<string, object> ret = new Dictionary<string, object>();
486 
487  XmlNodeList partL = element.ChildNodes;
488 
489  foreach (XmlNode part in partL)
490  {
491  XmlNode type = part.Attributes.GetNamedItem("type");
492  if (type == null || type.Value != "List")
493  {
494  ret[XmlConvert.DecodeName(part.Name)] = part.InnerText;
495  }
496  else
497  {
498  ret[XmlConvert.DecodeName(part.Name)] = ParseElement(part);
499  }
500  }
501 
502  return ret;
503  }
504 
505  public static IConfig GetConfig(string configFile, string configName)
506  {
507  IConfig config;
508 
509  if (File.Exists(configFile))
510  {
511  IConfigSource configsource = new IniConfigSource(configFile);
512  config = configsource.Configs[configName];
513  }
514  else
515  config = null;
516 
517  return config;
518  }
519 
520  public static IConfigSource LoadInitialConfig(string url)
521  {
522  IConfigSource source = new XmlConfigSource();
523  m_log.InfoFormat("[SERVER UTILS]: {0} is a http:// URI, fetching ...", url);
524 
525  // The ini file path is a http URI
526  // Try to read it
527  try
528  {
529  XmlReader r = XmlReader.Create(url);
530  IConfigSource cs = new XmlConfigSource(r);
531  source.Merge(cs);
532  }
533  catch (Exception e)
534  {
535  m_log.FatalFormat("[SERVER UTILS]: Exception reading config from URI {0}\n" + e.ToString(), url);
536  Environment.Exit(1);
537  }
538 
539  return source;
540  }
541  }
542 }
Command manager - Wrapper for OpenSim.Framework.PluginManager to allow us to add commands to the cons...
Interface to OpenSimulator's built in HTTP server. Use this to register handlers (http, llsd, xmlrpc, etc.) for given URLs.
Definition: IHttpServer.cs:36
void suppress_console_output_(bool save)
Definition: ServerUtils.cs:111
PluginLoader(IConfigSource config, string registryPath)
Definition: ServerUtils.cs:87