1 /*
2  *  Copyright 2005-2009 Fabrice Colin
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include <iostream>
20 
21 #include "XapianDatabaseFactory.h"
22 
23 using std::clog;
24 using std::endl;
25 using std::string;
26 using std::map;
27 using std::pair;
28 
29 pthread_mutex_t XapianDatabaseFactory::m_mutex = PTHREAD_MUTEX_INITIALIZER;
30 map<string, XapianDatabase *> XapianDatabaseFactory::m_databases;
31 bool XapianDatabaseFactory::m_closed = false;
32 
XapianDatabaseFactory()33 XapianDatabaseFactory::XapianDatabaseFactory()
34 {
35 }
36 
~XapianDatabaseFactory()37 XapianDatabaseFactory::~XapianDatabaseFactory()
38 {
39 }
40 
41 /// Merges two databases together and add the result to the list.
mergeDatabases(const string & name,XapianDatabase * pFirst,XapianDatabase * pSecond)42 bool XapianDatabaseFactory::mergeDatabases(const string &name,
43 	XapianDatabase *pFirst, XapianDatabase *pSecond)
44 {
45 	if (m_closed == true)
46 	{
47 		return false;
48 	}
49 
50 	map<string, XapianDatabase *>::iterator dbIter = m_databases.find(name);
51 	if (dbIter != m_databases.end())
52 	{
53 		return false;
54 	}
55 
56 	// Create the new database
57 	XapianDatabase *pDb = new XapianDatabase(name, pFirst, pSecond);
58 
59 	// Insert it into the map
60 	pair<map<string, XapianDatabase *>::iterator, bool> insertPair = m_databases.insert(pair<string, XapianDatabase *>(name, pDb));
61 	// Was it inserted ?
62 	if (insertPair.second == false)
63 	{
64 		// No, it wasn't : delete the object
65 		delete pDb;
66 
67 		return false;
68 	}
69 
70 	return true;
71 }
72 
73 /// Returns a XapianDatabase pointer; NULL if unavailable.
getDatabase(const string & location,bool readOnly,bool overwrite)74 XapianDatabase *XapianDatabaseFactory::getDatabase(const string &location,
75 	bool readOnly, bool overwrite)
76 {
77 	XapianDatabase *pDb = NULL;
78 
79 	if ((m_closed == true) ||
80 		(location.empty() == true))
81 	{
82 		return NULL;
83 	}
84 
85 	// Lock the map
86 	if (pthread_mutex_lock(&m_mutex) != 0)
87 	{
88 		return NULL;
89 	}
90 
91 	// Is the database already open ?
92 	map<string, XapianDatabase *>::iterator dbIter = m_databases.find(location);
93 	if (dbIter != m_databases.end())
94 	{
95 		pDb = dbIter->second;
96 
97 		// Overwrite the database ?
98 		if (overwrite == true)
99 		{
100 			dbIter->second = NULL;
101 #ifdef DEBUG
102 			clog << "XapianDatabaseFactory::getDatabase: closing " << dbIter->first << endl;
103 #endif
104 			m_databases.erase(dbIter);
105 			delete pDb;
106 
107 			dbIter = m_databases.end();
108 		}
109 	}
110 
111 	// Open the database ?
112 	if (dbIter == m_databases.end())
113 	{
114 		// Create a new instance
115 		pDb = new XapianDatabase(location, readOnly, overwrite);
116 		// Insert it into the map
117 		pair<map<string, XapianDatabase *>::iterator, bool> insertPair = m_databases.insert(pair<string, XapianDatabase *>(location, pDb));
118 		// Was it inserted ?
119 		if (insertPair.second == false)
120 		{
121 			// No, it wasn't : delete the object
122 			delete pDb;
123 			pDb = NULL;
124 		}
125 	}
126 
127 	// Unlock the map
128 	pthread_mutex_unlock(&m_mutex);
129 
130 	return pDb;
131 }
132 
133 /// Closes all databases.
closeAll(void)134 void XapianDatabaseFactory::closeAll(void)
135 {
136 	if (m_databases.empty() == true)
137 	{
138 		return;
139 	}
140 
141 	// Lock the map
142 	// FIXME: another thread may have a database and try and lock it after the loop below deletes it
143 	if (pthread_mutex_lock(&m_mutex) != 0)
144 	{
145 		return;
146 	}
147 	m_closed = true;
148 
149 	// Close merged databases first
150 	std::map<std::string, XapianDatabase *>::iterator dbIter = m_databases.begin();
151 	while (dbIter != m_databases.end())
152 	{
153 		XapianDatabase *pDb = dbIter->second;
154 
155 		if (pDb->isMerge() == false)
156 		{
157 			++dbIter;
158 			continue;
159 		}
160 
161 		std::map<std::string, XapianDatabase *>::iterator nextIter = dbIter;
162 		++nextIter;
163 #ifdef DEBUG
164 		clog << "XapianDatabaseFactory::closeAll: closing " << dbIter->first << endl;
165 #endif
166 
167 		// Remove from the map
168 		dbIter->second = NULL;
169 		m_databases.erase(dbIter);
170 
171 		Xapian::Database *pIndex = pDb->readLock();
172 		pDb->unlock();
173 		// Close the database
174 		delete pDb;
175 
176 		dbIter = nextIter;
177 	}
178 	// Now close all other databases
179 	dbIter = m_databases.begin();
180 	while (dbIter != m_databases.end())
181 	{
182 		XapianDatabase *pDb = dbIter->second;
183 		Xapian::Database *pIndex = NULL;
184 #ifdef DEBUG
185 		clog << "XapianDatabaseFactory::closeAll: closing " << dbIter->first << endl;
186 #endif
187 
188 		// Remove from the map
189 		dbIter->second = NULL;
190 		m_databases.erase(dbIter);
191 
192 		if (pDb->isWritable() == true)
193 		{
194 			pIndex = pDb->writeLock();
195 		}
196 		else
197 		{
198 			pIndex = pDb->readLock();
199 		}
200 		pDb->unlock();
201 		// Close the database
202 		delete pDb;
203 
204 		dbIter = m_databases.begin();
205 	}
206 
207 	// Unlock the map
208 	pthread_mutex_unlock(&m_mutex);
209 }
210