/* * 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 OpenSim 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; using System.Collections.Generic; using System.IO; using System.Reflection; using System.Text.RegularExpressions; using OpenMetaverse; using log4net; using NHibernate; using NHibernate.Cfg; using NHibernate.Expression; using NHibernate.Mapping.Attributes; using NHibernate.Tool.hbm2ddl; using OpenSim.Framework; using OpenSim.Region.Environment.Interfaces; using OpenSim.Region.Environment.Scenes; using Environment=NHibernate.Cfg.Environment; namespace OpenSim.Data.NHibernate { /// /// A RegionData Interface to the NHibernate database /// public class NHibernateRegionData : IRegionDataStore { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private Configuration cfg; private ISessionFactory factory; private ISession session; // public void Initialise() // { // Initialise("SQLiteDialect;SqliteClientDriver;URI=file:OpenSim.db,version=3", true); // } public void Initialise(string connect) { // Split out the dialect, driver, and connect string char[] split = {';'}; string[] parts = connect.Split(split, 3); if (parts.Length != 3) { // TODO: make this a real exception type throw new Exception("Malformed Region connection string '" + connect + "'"); } string dialect = parts[0]; // NHibernate setup cfg = new Configuration(); cfg.SetProperty(Environment.ConnectionProvider, "NHibernate.Connection.DriverConnectionProvider"); cfg.SetProperty(Environment.Dialect, "NHibernate.Dialect." + dialect); cfg.SetProperty(Environment.ConnectionDriver, "NHibernate.Driver." + parts[1]); cfg.SetProperty(Environment.ConnectionString, parts[2]); cfg.AddAssembly("OpenSim.Data.NHibernate"); HbmSerializer.Default.Validate = true; using (MemoryStream stream = HbmSerializer.Default.Serialize(Assembly.GetExecutingAssembly())) cfg.AddInputStream(stream); factory = cfg.BuildSessionFactory(); session = factory.OpenSession(); // This actually does the roll forward assembly stuff Assembly assem = GetType().Assembly; Migration m = new Migration((System.Data.Common.DbConnection)factory.ConnectionProvider.GetConnection(), assem, dialect, "RegionStore"); m.Update(); } /*********************************************************************** * * Public Interface Functions * **********************************************************************/ public void StoreRegionSettings(RegionSettings rs) { } public RegionSettings LoadRegionSettings(UUID regionUUID) { return null; } // This looks inefficient, but it turns out that it isn't // based on trial runs with nhibernate 1.2 private void SaveOrUpdate(SceneObjectPart p) { try { SceneObjectPart old = session.Load(typeof(SceneObjectPart), p.UUID) as SceneObjectPart; session.Evict(old); session.Update(p); m_log.InfoFormat("[NHIBERNATE] updating object {0}", p.UUID); } catch (ObjectNotFoundException) { m_log.InfoFormat("[NHIBERNATE] saving object {0}", p.UUID); session.Save(p); } catch (Exception e) { m_log.Error("[NHIBERNATE] issue saving part", e); } } private void SaveOrUpdate(Terrain t) { try { Terrain old = session.Load(typeof(Terrain), t.RegionID) as Terrain; session.Evict(old); session.Update(t); session.Flush(); } catch (ObjectNotFoundException) { session.Save(t); session.Flush(); } catch (Exception e) { m_log.Error("[NHIBERNATE] issue saving terrain", e); } } /// /// Adds an object into region storage /// /// the object /// the region UUID public void StoreObject(SceneObjectGroup obj, UUID regionUUID) { try { foreach (SceneObjectPart part in obj.Children.Values) { m_log.InfoFormat("Storing part {0}", part.UUID); SaveOrUpdate(part); } session.Flush(); } catch (Exception e) { m_log.Error("Can't save: ", e); } } private SceneObjectGroup LoadObject(UUID uuid, UUID region) { SceneObjectGroup group = new SceneObjectGroup(); ICriteria criteria = session.CreateCriteria(typeof(SceneObjectPart)); criteria.Add(Expression.Eq("RegionID", region)); criteria.Add(Expression.Eq("ParentUUID", uuid)); criteria.AddOrder( Order.Asc("ParentID") ); foreach (SceneObjectPart p in criteria.List()) { // root part if (p.UUID == uuid) { group.AddPart(p); group.RootPart = p; } else { group.AddPart(p); } } return group; } /// /// Removes an object from region storage /// /// the object /// the region UUID public void RemoveObject(UUID obj, UUID regionUUID) { SceneObjectGroup g = LoadObject(obj, regionUUID); foreach (SceneObjectPart p in g.Children.Values) { session.Delete(p); } session.Flush(); m_log.InfoFormat("[REGION DB]: Removing obj: {0} from region: {1}", obj.Guid, regionUUID); } /// /// Load persisted objects from region storage. /// /// The region UUID /// List of loaded groups public List LoadObjects(UUID regionUUID) { Dictionary SOG = new Dictionary(); List ret = new List(); ICriteria criteria = session.CreateCriteria(typeof(SceneObjectPart)); criteria.Add(Expression.Eq("RegionID", regionUUID)); criteria.AddOrder( Order.Asc("ParentID") ); foreach (SceneObjectPart p in criteria.List()) { // root part if (p.UUID == p.ParentUUID) { SceneObjectGroup group = new SceneObjectGroup(); group.AddPart(p); group.RootPart = p; SOG.Add(p.ParentUUID, group); } else { SOG[p.ParentUUID].AddPart(p); } // get the inventory ICriteria InvCriteria = session.CreateCriteria(typeof(TaskInventoryItem)); InvCriteria.Add(Expression.Eq("ParentPartID", p.UUID)); IList inventory = new List(); foreach (TaskInventoryItem i in InvCriteria.List()) { inventory.Add(i); } if (inventory.Count > 0) p.RestoreInventoryItems(inventory); } foreach (SceneObjectGroup g in SOG.Values) { ret.Add(g); } return ret; } /// /// Store a terrain revision in region storage /// /// terrain heightfield /// region UUID public void StoreTerrain(double[,] ter, UUID regionID) { lock (this) { Terrain t = new Terrain(regionID, ter); SaveOrUpdate(t); } } /// /// Load the latest terrain revision from region storage /// /// the region UUID /// Heightfield data public double[,] LoadTerrain(UUID regionID) { try { Terrain t = session.Load(typeof(Terrain), regionID) as Terrain; return t.Doubles; } catch (ObjectNotFoundException) { m_log.Info("No terrain yet"); return null; } } /// /// /// /// public void RemoveLandObject(UUID globalID) { } /// /// /// /// public void StoreLandObject(ILandObject parcel) { } /// /// /// /// /// public List LoadLandObjects(UUID regionUUID) { List landDataForRegion = new List(); return landDataForRegion; } /// /// See /// public void Shutdown() { session.Flush(); } /// /// Load a region banlist /// /// the region UUID /// The banlist public List LoadRegionBanList(UUID regionUUID) { List regionbanlist = new List(); return regionbanlist; } /// /// Add en entry into region banlist /// /// public void AddToRegionBanlist(EstateBan item) { } /// /// remove an entry from the region banlist /// /// public void RemoveFromRegionBanlist(EstateBan item) { } /// /// /// /// /// // private static Array serializeTerrain(double[,] val) // { // MemoryStream str = new MemoryStream(65536*sizeof (double)); // BinaryWriter bw = new BinaryWriter(str); // // // TODO: COMPATIBILITY - Add byte-order conversions // for (int x = 0; x < 256; x++) // for (int y = 0; y < 256; y++) // bw.Write(val[x, y]); // // return str.ToArray(); // } /// /// see IRegionDatastore /// /// /// public void StorePrimInventory(UUID primID, ICollection items) { ICriteria criteria = session.CreateCriteria(typeof(TaskInventoryItem)); criteria.Add(Expression.Eq("ParentPartID", primID)); try { foreach (TaskInventoryItem i in criteria.List()) { session.Delete(i); } foreach (TaskInventoryItem i in items) { session.Save(i); } session.Flush(); } catch (Exception e) { m_log.Error("[NHIBERNATE] StoreInvetory", e); } } } }