17 |
ilm |
1 |
/*
|
|
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
|
|
3 |
*
|
182 |
ilm |
4 |
* Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
|
17 |
ilm |
5 |
*
|
|
|
6 |
* The contents of this file are subject to the terms of the GNU General Public License Version 3
|
|
|
7 |
* only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
|
|
|
8 |
* copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
|
|
|
9 |
* language governing permissions and limitations under the License.
|
|
|
10 |
*
|
|
|
11 |
* When distributing the software, include this License Header Notice in each file.
|
|
|
12 |
*/
|
|
|
13 |
|
|
|
14 |
/*
|
|
|
15 |
* Created on 7 mai 03
|
|
|
16 |
*/
|
|
|
17 |
package org.openconcerto.sql.model;
|
|
|
18 |
|
63 |
ilm |
19 |
import org.openconcerto.sql.model.LoadingListener.StructureLoadingEvent;
|
67 |
ilm |
20 |
import org.openconcerto.sql.model.graph.TablesMap;
|
17 |
ilm |
21 |
import org.openconcerto.sql.utils.SQL_URL;
|
|
|
22 |
import org.openconcerto.utils.CollectionUtils;
|
182 |
ilm |
23 |
import org.openconcerto.utils.NetUtils;
|
|
|
24 |
import org.openconcerto.utils.Tuple2.List2;
|
61 |
ilm |
25 |
import org.openconcerto.utils.cc.CopyOnWriteMap;
|
17 |
ilm |
26 |
import org.openconcerto.utils.cc.IClosure;
|
|
|
27 |
import org.openconcerto.utils.cc.ITransformer;
|
|
|
28 |
import org.openconcerto.utils.change.CollectionChangeEventCreator;
|
|
|
29 |
|
80 |
ilm |
30 |
import java.security.AccessController;
|
|
|
31 |
import java.security.PrivilegedAction;
|
63 |
ilm |
32 |
import java.sql.Connection;
|
17 |
ilm |
33 |
import java.sql.SQLException;
|
|
|
34 |
import java.util.ArrayList;
|
|
|
35 |
import java.util.Collection;
|
|
|
36 |
import java.util.Collections;
|
67 |
ilm |
37 |
import java.util.HashMap;
|
17 |
ilm |
38 |
import java.util.HashSet;
|
|
|
39 |
import java.util.List;
|
|
|
40 |
import java.util.Map;
|
|
|
41 |
import java.util.Set;
|
65 |
ilm |
42 |
import java.util.concurrent.Callable;
|
17 |
ilm |
43 |
|
61 |
ilm |
44 |
import net.jcip.annotations.GuardedBy;
|
|
|
45 |
|
17 |
ilm |
46 |
/**
|
|
|
47 |
* Un serveur de base de donnée SQL. Meaning a system (eg mysql) on a certain host and port. Un
|
|
|
48 |
* serveur permet d'accéder aux bases qui le composent (une base par défaut peut être spécifiée). De
|
|
|
49 |
* plus il permet de spécifier un login/pass par défaut.
|
|
|
50 |
*
|
|
|
51 |
* @author ilm
|
61 |
ilm |
52 |
* @see #getOrCreateBase(String)
|
17 |
ilm |
53 |
*/
|
|
|
54 |
public final class SQLServer extends DBStructureItemJDBC {
|
|
|
55 |
|
61 |
ilm |
56 |
private static final IClosure<SQLDataSource> DSINIT_ERROR = new IClosure<SQLDataSource>() {
|
|
|
57 |
@Override
|
|
|
58 |
public void executeChecked(SQLDataSource input) {
|
|
|
59 |
throw new IllegalStateException("Datasource should already be created");
|
|
|
60 |
}
|
|
|
61 |
};
|
|
|
62 |
|
83 |
ilm |
63 |
static private final <T> IClosure<? super T> coalesce(IClosure<? super T> o1, IClosure<? super T> o2) {
|
|
|
64 |
// ternary operator makes Eclipse fail
|
|
|
65 |
if (o1 != null)
|
|
|
66 |
return o1;
|
|
|
67 |
else
|
|
|
68 |
return o2;
|
|
|
69 |
}
|
|
|
70 |
|
17 |
ilm |
71 |
public static final DBSystemRoot create(final SQL_URL url) {
|
|
|
72 |
return create(url, Collections.<String> emptySet(), null);
|
|
|
73 |
}
|
|
|
74 |
|
|
|
75 |
/**
|
|
|
76 |
* Create a system root from the passed URL.
|
|
|
77 |
*
|
|
|
78 |
* @param url an SQL URL.
|
65 |
ilm |
79 |
* @param rootsToMap the collection of {@link DBSystemRoot#setRootsToMap(Collection) roots to
|
|
|
80 |
* map}, in addition to <code>url.{@link SQL_URL#getRootName() getRootName()}</code>.
|
17 |
ilm |
81 |
* @param dsInit to initialize the datasource before any request (e.g. setting JDBC properties),
|
|
|
82 |
* can be <code>null</code>.
|
|
|
83 |
* @return the new system root.
|
|
|
84 |
*/
|
|
|
85 |
public static final DBSystemRoot create(final SQL_URL url, final Collection<String> rootsToMap, IClosure<SQLDataSource> dsInit) {
|
|
|
86 |
return create(url, rootsToMap, false, dsInit);
|
|
|
87 |
}
|
|
|
88 |
|
|
|
89 |
public static final DBSystemRoot create(final SQL_URL url, final Collection<String> roots, final boolean setPath, IClosure<SQLDataSource> dsInit) {
|
|
|
90 |
final DBSystemRoot res = create(url, new IClosure<DBSystemRoot>() {
|
|
|
91 |
@Override
|
|
|
92 |
public void executeChecked(DBSystemRoot input) {
|
93 |
ilm |
93 |
if (url.getRootName() != null) {
|
|
|
94 |
input.setRootToMap(url.getRootName());
|
|
|
95 |
input.addRootsToMap(roots);
|
|
|
96 |
} else {
|
|
|
97 |
input.setRootsToMap(roots);
|
|
|
98 |
}
|
17 |
ilm |
99 |
}
|
|
|
100 |
}, dsInit);
|
|
|
101 |
if (setPath) {
|
|
|
102 |
final List<String> path = new ArrayList<String>(roots);
|
93 |
ilm |
103 |
if (url.getRootName() != null)
|
|
|
104 |
path.add(0, url.getRootName());
|
17 |
ilm |
105 |
path.retainAll(res.getChildrenNames());
|
|
|
106 |
if (path.size() > 0)
|
|
|
107 |
res.setRootPath(path);
|
|
|
108 |
}
|
|
|
109 |
return res;
|
|
|
110 |
}
|
|
|
111 |
|
83 |
ilm |
112 |
public static final DBSystemRoot create(final SQL_URL url, final IClosure<? super DBSystemRoot> systemRootInit, final IClosure<? super SQLDataSource> dsInit) {
|
65 |
ilm |
113 |
return new SQLServer(url.getSystem(), url.getServerName(), null, url.getLogin(), url.getPass(), systemRootInit, dsInit).getSystemRoot(url.getSystemRootName());
|
17 |
ilm |
114 |
}
|
|
|
115 |
|
|
|
116 |
// *** Instance members
|
|
|
117 |
|
|
|
118 |
// eg mysql, derby
|
|
|
119 |
private final SQLSystem system;
|
|
|
120 |
private final String login;
|
|
|
121 |
private final String pass;
|
83 |
ilm |
122 |
private final IClosure<? super DBSystemRoot> systemRootInit;
|
61 |
ilm |
123 |
@GuardedBy("baseMutex")
|
|
|
124 |
private CopyOnWriteMap<String, SQLBase> bases;
|
|
|
125 |
private Object baseMutex = new String("base mutex");
|
|
|
126 |
// linked to bases
|
|
|
127 |
@GuardedBy("this")
|
17 |
ilm |
128 |
private String defaultBase;
|
61 |
ilm |
129 |
// linked to dsSet
|
|
|
130 |
@GuardedBy("this")
|
17 |
ilm |
131 |
private SQLDataSource ds;
|
61 |
ilm |
132 |
@GuardedBy("this")
|
17 |
ilm |
133 |
private boolean dsSet;
|
83 |
ilm |
134 |
private final IClosure<? super SQLDataSource> dsInit;
|
17 |
ilm |
135 |
private final ITransformer<String, String> urlTransf;
|
|
|
136 |
|
67 |
ilm |
137 |
@GuardedBy("this")
|
|
|
138 |
private String id;
|
|
|
139 |
|
65 |
ilm |
140 |
public SQLServer(SQLSystem system, String host) {
|
17 |
ilm |
141 |
this(system, host, null);
|
|
|
142 |
}
|
|
|
143 |
|
65 |
ilm |
144 |
public SQLServer(SQLSystem system, String host, String port) {
|
17 |
ilm |
145 |
this(system, host, port, null, null);
|
|
|
146 |
}
|
|
|
147 |
|
65 |
ilm |
148 |
public SQLServer(SQLSystem system, String host, String port, String login, String pass) {
|
17 |
ilm |
149 |
this(system, host, port, login, pass, null, null);
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
/**
|
|
|
153 |
* Creates a new server.
|
|
|
154 |
*
|
65 |
ilm |
155 |
* @param system the database system.
|
17 |
ilm |
156 |
* @param host an IP address or DNS name.
|
|
|
157 |
* @param port the port to connect to can be <code>null</code> to pick the system default.
|
|
|
158 |
* @param login the default login to access database of this server, can be <code>null</code>.
|
|
|
159 |
* @param pass the default password to access database of this server, can be <code>null</code>.
|
|
|
160 |
* @param systemRootInit to initialize the system root in its constructor, can be
|
|
|
161 |
* <code>null</code>.
|
|
|
162 |
* @param dsInit to initialize the datasource before any request (e.g. setting JDBC properties),
|
61 |
ilm |
163 |
* must be thread-safe, can be <code>null</code>.
|
17 |
ilm |
164 |
*/
|
83 |
ilm |
165 |
public SQLServer(SQLSystem system, String host, String port, String login, String pass, IClosure<? super DBSystemRoot> systemRootInit, IClosure<? super SQLDataSource> dsInit) {
|
17 |
ilm |
166 |
super(null, host);
|
|
|
167 |
this.ds = null;
|
|
|
168 |
this.dsSet = false;
|
|
|
169 |
this.dsInit = dsInit;
|
65 |
ilm |
170 |
this.system = system;
|
17 |
ilm |
171 |
this.login = login;
|
|
|
172 |
this.pass = pass;
|
|
|
173 |
this.bases = null;
|
|
|
174 |
this.systemRootInit = systemRootInit;
|
|
|
175 |
this.urlTransf = this.getSQLSystem().getURLTransf(this);
|
|
|
176 |
|
67 |
ilm |
177 |
this.id = this.getName();
|
|
|
178 |
|
17 |
ilm |
179 |
// cannot refetch now as we don't have any datasource yet (see createSystemRoot())
|
|
|
180 |
}
|
|
|
181 |
|
61 |
ilm |
182 |
private final CopyOnWriteMap<String, SQLBase> getBases() {
|
|
|
183 |
synchronized (this.getTreeMutex()) {
|
|
|
184 |
synchronized (this.baseMutex) {
|
|
|
185 |
if (this.bases == null) {
|
|
|
186 |
this.checkDropped();
|
|
|
187 |
this.bases = new CopyOnWriteMap<String, SQLBase>();
|
65 |
ilm |
188 |
this.refresh(null, true, true);
|
61 |
ilm |
189 |
}
|
|
|
190 |
return this.bases;
|
|
|
191 |
}
|
17 |
ilm |
192 |
}
|
|
|
193 |
}
|
|
|
194 |
|
|
|
195 |
/**
|
|
|
196 |
* Signal that this server and its descendants will not be used anymore.
|
|
|
197 |
*/
|
|
|
198 |
public final void destroy() {
|
83 |
ilm |
199 |
synchronized (this.getTreeMutex()) {
|
|
|
200 |
if (!this.isDropped())
|
|
|
201 |
this.dropped();
|
|
|
202 |
}
|
17 |
ilm |
203 |
}
|
|
|
204 |
|
|
|
205 |
@Override
|
|
|
206 |
protected void onDrop() {
|
61 |
ilm |
207 |
synchronized (this) {
|
|
|
208 |
if (this.ds != null)
|
|
|
209 |
try {
|
|
|
210 |
this.ds.close();
|
|
|
211 |
} catch (SQLException e) {
|
|
|
212 |
// tant pis
|
|
|
213 |
e.printStackTrace();
|
|
|
214 |
}
|
|
|
215 |
}
|
17 |
ilm |
216 |
// allow SQLBase to be gc'd even if someone holds on to us
|
61 |
ilm |
217 |
synchronized (this.baseMutex) {
|
|
|
218 |
this.bases = null;
|
|
|
219 |
}
|
17 |
ilm |
220 |
super.onDrop();
|
|
|
221 |
}
|
|
|
222 |
|
61 |
ilm |
223 |
private final Object getTreeMutex() {
|
|
|
224 |
final DBSystemRoot sysRoot = this.getDBSystemRoot();
|
|
|
225 |
final Object res = sysRoot == null ? this : sysRoot.getTreeMutex();
|
|
|
226 |
assert Thread.holdsLock(res) || !Thread.holdsLock(this);
|
|
|
227 |
return res;
|
|
|
228 |
}
|
|
|
229 |
|
67 |
ilm |
230 |
Map<String, TablesMap> refresh(final Map<String, TablesMap> namesToRefresh, final boolean readCache) {
|
|
|
231 |
return this.refresh(namesToRefresh, readCache, false);
|
17 |
ilm |
232 |
}
|
|
|
233 |
|
67 |
ilm |
234 |
// return null if this cannot be refreshed (i.e. this is above DBSystemRoot)
|
|
|
235 |
private Map<String, TablesMap> refresh(final Map<String, TablesMap> tablesToRefresh, final boolean readCache, final boolean init) {
|
17 |
ilm |
236 |
if (this.getDS() != null) {
|
67 |
ilm |
237 |
if (Collections.emptyMap().equals(tablesToRefresh))
|
|
|
238 |
return tablesToRefresh;
|
|
|
239 |
final Set<String> namesToRefresh = tablesToRefresh == null ? null : tablesToRefresh.keySet();
|
17 |
ilm |
240 |
// for mysql we must know our children, since they can reference each other and thus the
|
|
|
241 |
// graph needs them
|
63 |
ilm |
242 |
final StructureLoadingEvent evt = new StructureLoadingEvent(this, namesToRefresh);
|
17 |
ilm |
243 |
try {
|
63 |
ilm |
244 |
this.getDBSystemRoot().fireLoading(evt);
|
61 |
ilm |
245 |
synchronized (this.getTreeMutex()) {
|
|
|
246 |
final Set<String> childrenToRefresh = CollectionUtils.inter(namesToRefresh, this.getChildrenNames());
|
|
|
247 |
// don't save the result in files since getCatalogs() is at least as quick as
|
|
|
248 |
// executing a request to check if the cache is obsolete
|
63 |
ilm |
249 |
final Set<String> allCats;
|
|
|
250 |
final Connection conn = this.getDS().getNewConnection();
|
|
|
251 |
try {
|
142 |
ilm |
252 |
final List<String> allCatsList = ColumnListHandlerGeneric.create(String.class).handle(conn.getMetaData().getCatalogs());
|
63 |
ilm |
253 |
allCats = new HashSet<String>(allCatsList);
|
|
|
254 |
} finally {
|
|
|
255 |
this.getDS().returnConnection(conn);
|
|
|
256 |
}
|
|
|
257 |
final Set<String> cats = CollectionUtils.inter(namesToRefresh, allCats);
|
61 |
ilm |
258 |
this.getDBSystemRoot().filterNodes(this, cats);
|
17 |
ilm |
259 |
|
61 |
ilm |
260 |
SQLBase.mustContain(this, cats, childrenToRefresh, "bases");
|
|
|
261 |
for (final String base : CollectionUtils.substract(childrenToRefresh, cats)) {
|
|
|
262 |
final CollectionChangeEventCreator c = this.createChildrenCreator();
|
|
|
263 |
final SQLBase existingBase = this.getBases().remove(base);
|
|
|
264 |
this.fireChildrenChanged(c);
|
|
|
265 |
// null if it was never created
|
|
|
266 |
if (existingBase != null)
|
|
|
267 |
existingBase.dropped();
|
|
|
268 |
}
|
|
|
269 |
// delete the saved bases that we could have fetched, but haven't
|
|
|
270 |
// (bases that are not in scope are simply ignored, NOT deleted)
|
|
|
271 |
final DBFileCache cache = this.getFileCache();
|
|
|
272 |
if (cache != null) {
|
|
|
273 |
for (final DBItemFileCache savedBase : cache.getServerCache().getSavedDesc(SQLBase.class)) {
|
|
|
274 |
final String savedBaseName = savedBase.getName();
|
|
|
275 |
if (!cats.contains(savedBaseName) && (namesToRefresh == null || namesToRefresh.contains(savedBaseName)) && this.getDBSystemRoot().createNode(this, savedBaseName)) {
|
80 |
ilm |
276 |
AccessController.doPrivileged(new PrivilegedAction<Object>() {
|
|
|
277 |
@Override
|
|
|
278 |
public Object run() {
|
|
|
279 |
savedBase.delete();
|
|
|
280 |
return null;
|
|
|
281 |
}
|
|
|
282 |
});
|
61 |
ilm |
283 |
}
|
17 |
ilm |
284 |
}
|
|
|
285 |
}
|
|
|
286 |
|
65 |
ilm |
287 |
// fire once all the bases are loaded so that the graph is coherent
|
67 |
ilm |
288 |
return this.getDBSystemRoot().getGraph().atomicRefresh(new Callable<Map<String, TablesMap>>() {
|
182 |
ilm |
289 |
|
65 |
ilm |
290 |
@Override
|
67 |
ilm |
291 |
public Map<String, TablesMap> call() throws Exception {
|
|
|
292 |
final Map<String, TablesMap> res = new HashMap<String, TablesMap>();
|
65 |
ilm |
293 |
// add or refresh
|
|
|
294 |
for (final String cat : cats) {
|
67 |
ilm |
295 |
final TablesMap jdbcTables;
|
65 |
ilm |
296 |
final SQLBase existing = getBase(cat);
|
67 |
ilm |
297 |
if (existing != null) {
|
|
|
298 |
jdbcTables = existing.refresh(tablesToRefresh == null ? null : tablesToRefresh.get(cat), readCache);
|
|
|
299 |
} else {
|
|
|
300 |
// ignore tablesToRefresh if the base didn't exist (see
|
|
|
301 |
// SQLBase.assureAllTables())
|
65 |
ilm |
302 |
// we already have the datasource, so login/pass aren't used
|
83 |
ilm |
303 |
jdbcTables = createBase(cat, null, "", "", DSINIT_ERROR, readCache);
|
67 |
ilm |
304 |
}
|
|
|
305 |
if (!jdbcTables.isEmpty())
|
|
|
306 |
res.put(cat, jdbcTables);
|
65 |
ilm |
307 |
}
|
67 |
ilm |
308 |
return res;
|
65 |
ilm |
309 |
}
|
|
|
310 |
});
|
17 |
ilm |
311 |
}
|
|
|
312 |
} catch (SQLException e) {
|
|
|
313 |
throw new IllegalStateException("could not get children names", e);
|
63 |
ilm |
314 |
} finally {
|
|
|
315 |
this.getDBSystemRoot().fireLoading(evt.createFinishingEvent());
|
17 |
ilm |
316 |
}
|
61 |
ilm |
317 |
} else if (!init) {
|
|
|
318 |
throw new IllegalArgumentException("Cannot create bases since this server cannot have a connection");
|
17 |
ilm |
319 |
}
|
67 |
ilm |
320 |
return null;
|
17 |
ilm |
321 |
}
|
|
|
322 |
|
|
|
323 |
/**
|
|
|
324 |
* Copy constructor. The new instance is in the same state <code>s</code> was, when it was
|
|
|
325 |
* created (no SQLBase, no default base).
|
|
|
326 |
*
|
|
|
327 |
* @param s the server to copy from.
|
|
|
328 |
*/
|
|
|
329 |
public SQLServer(SQLServer s) {
|
65 |
ilm |
330 |
this(s.system, s.getName(), null, s.login, s.pass);
|
17 |
ilm |
331 |
}
|
|
|
332 |
|
|
|
333 |
// tries to get a ds without any db
|
61 |
ilm |
334 |
private synchronized final SQLDataSource getDS() {
|
17 |
ilm |
335 |
if (!this.dsSet) {
|
61 |
ilm |
336 |
this.checkDropped();
|
17 |
ilm |
337 |
final DBSystemRoot sysRoot = this.getDBSystemRoot();
|
|
|
338 |
if (sysRoot == null) {
|
|
|
339 |
this.ds = null;
|
|
|
340 |
} else {
|
|
|
341 |
// should not succeed if pb otherwise with dsSet
|
|
|
342 |
// it will never be called again
|
|
|
343 |
this.ds = sysRoot.getDataSource();
|
|
|
344 |
}
|
|
|
345 |
this.dsSet = true;
|
|
|
346 |
}
|
|
|
347 |
return this.ds;
|
|
|
348 |
}
|
|
|
349 |
|
|
|
350 |
final String getURL(String base) {
|
|
|
351 |
return this.urlTransf.transformChecked(base);
|
|
|
352 |
}
|
|
|
353 |
|
|
|
354 |
/**
|
|
|
355 |
* Retourne la base par défaut.
|
|
|
356 |
*
|
|
|
357 |
* @return la base par défaut.
|
|
|
358 |
* @see #setDefaultBase(String)
|
|
|
359 |
*/
|
|
|
360 |
public SQLBase getBase() {
|
61 |
ilm |
361 |
final String def;
|
|
|
362 |
synchronized (this) {
|
|
|
363 |
def = this.defaultBase;
|
|
|
364 |
}
|
|
|
365 |
if (def == null) {
|
17 |
ilm |
366 |
throw new IllegalStateException("default base unset");
|
|
|
367 |
}
|
61 |
ilm |
368 |
return this.getBase(def);
|
17 |
ilm |
369 |
}
|
|
|
370 |
|
61 |
ilm |
371 |
public SQLBase getBase(String baseName) {
|
|
|
372 |
return this.getBases().get(baseName);
|
|
|
373 |
}
|
|
|
374 |
|
17 |
ilm |
375 |
/**
|
|
|
376 |
* Return the specified base using default login/pass.
|
|
|
377 |
*
|
|
|
378 |
* @param baseName the name of base to be returned.
|
|
|
379 |
* @return the SQLBase named <i>baseName</i>.
|
|
|
380 |
* @see #getBase(String, String, String, IClosure)
|
|
|
381 |
*/
|
61 |
ilm |
382 |
public SQLBase getOrCreateBase(String baseName) {
|
17 |
ilm |
383 |
return this.getBase(baseName, null, null);
|
|
|
384 |
}
|
|
|
385 |
|
|
|
386 |
public SQLBase getBase(String baseName, String login, String pass) {
|
|
|
387 |
return this.getBase(baseName, login, pass, null);
|
|
|
388 |
}
|
|
|
389 |
|
|
|
390 |
/**
|
|
|
391 |
* Return the specified base using provided login/pass. Does nothing if there's already a base
|
|
|
392 |
* with this name.
|
|
|
393 |
*
|
|
|
394 |
* @param baseName the name of the base.
|
|
|
395 |
* @param login the login, <code>null</code> means default.
|
|
|
396 |
* @param pass the password, <code>null</code> means default.
|
|
|
397 |
* @param dsInit to initialize the datasource before any request (eg setting jdbc properties),
|
|
|
398 |
* <code>null</code> meaning take the server one.
|
|
|
399 |
* @return the corresponding base.
|
|
|
400 |
*/
|
83 |
ilm |
401 |
public SQLBase getBase(String baseName, String login, String pass, IClosure<? super SQLDataSource> dsInit) {
|
|
|
402 |
return this.getBase(baseName, null, login, pass, dsInit, true);
|
65 |
ilm |
403 |
}
|
|
|
404 |
|
83 |
ilm |
405 |
public SQLBase getBase(String baseName, final IClosure<? super DBSystemRoot> systemRootInit, String login, String pass, IClosure<? super SQLDataSource> dsInit, boolean readCache) {
|
67 |
ilm |
406 |
if (this.getDBSystemRoot() != null)
|
65 |
ilm |
407 |
throw new IllegalStateException("getBase(name, login, pass) should only be used for systems where SQLBase is DBSystemRoot");
|
61 |
ilm |
408 |
synchronized (this.getTreeMutex()) {
|
|
|
409 |
SQLBase base = this.getBase(baseName);
|
|
|
410 |
if (base == null) {
|
83 |
ilm |
411 |
this.createBase(baseName, systemRootInit, login, pass, dsInit, readCache);
|
67 |
ilm |
412 |
base = this.getBase(baseName);
|
61 |
ilm |
413 |
}
|
|
|
414 |
return base;
|
17 |
ilm |
415 |
}
|
|
|
416 |
}
|
|
|
417 |
|
83 |
ilm |
418 |
private final TablesMap createBase(String baseName, IClosure<? super DBSystemRoot> systemRootInit, String login, String pass, IClosure<? super SQLDataSource> dsInit, boolean readCache) {
|
67 |
ilm |
419 |
final DBSystemRoot sysRoot = this.getDBSystemRoot();
|
|
|
420 |
if (sysRoot != null && !sysRoot.createNode(this, baseName))
|
|
|
421 |
throw new IllegalStateException(baseName + " is filtered, you must add it to rootsToMap");
|
142 |
ilm |
422 |
final SQLBase base = new SQLBase(this, baseName, coalesce(systemRootInit, this.systemRootInit), login == null ? this.login : login, pass == null ? this.pass : pass,
|
|
|
423 |
coalesce(dsInit, this.dsInit));
|
67 |
ilm |
424 |
return this.putBase(baseName, base, readCache);
|
|
|
425 |
}
|
|
|
426 |
|
17 |
ilm |
427 |
public final DBSystemRoot getSystemRoot(String name) {
|
83 |
ilm |
428 |
return this.getSystemRoot(name, null, null, null, null);
|
17 |
ilm |
429 |
}
|
|
|
430 |
|
|
|
431 |
/**
|
|
|
432 |
* Return the specified systemRoot using provided login/pass. Does nothing if there's already a
|
|
|
433 |
* systemRoot with this name.
|
|
|
434 |
*
|
|
|
435 |
* @param name name of the system root, NOTE: for some systems the server is the systemRoot so
|
|
|
436 |
* <code>name</code> will be silently ignored.
|
83 |
ilm |
437 |
* @param systemRootInit to initialize the {@link DBSystemRoot} before setting the data source.
|
17 |
ilm |
438 |
* @param login the login, <code>null</code> means default.
|
|
|
439 |
* @param pass the password, <code>null</code> means default.
|
|
|
440 |
* @param dsInit to initialize the datasource before any request (eg setting jdbc properties),
|
|
|
441 |
* <code>null</code> meaning take the server one.
|
|
|
442 |
* @return the corresponding systemRoot.
|
|
|
443 |
* @see #isSystemRootCreated(String)
|
|
|
444 |
*/
|
83 |
ilm |
445 |
public final DBSystemRoot getSystemRoot(String name, final IClosure<? super DBSystemRoot> systemRootInit, String login, String pass, IClosure<? super SQLDataSource> dsInit) {
|
61 |
ilm |
446 |
synchronized (this.getTreeMutex()) {
|
|
|
447 |
if (!this.isSystemRootCreated(name)) {
|
83 |
ilm |
448 |
return this.createSystemRoot(name, systemRootInit, login, pass, dsInit);
|
61 |
ilm |
449 |
} else {
|
|
|
450 |
final DBSystemRoot res;
|
|
|
451 |
final DBSystemRoot sysRoot = this.getDBSystemRoot();
|
|
|
452 |
if (sysRoot != null)
|
|
|
453 |
res = sysRoot;
|
|
|
454 |
else {
|
|
|
455 |
res = this.getBase(name).getDBSystemRoot();
|
|
|
456 |
}
|
|
|
457 |
return res;
|
17 |
ilm |
458 |
}
|
|
|
459 |
}
|
|
|
460 |
}
|
|
|
461 |
|
83 |
ilm |
462 |
private final DBSystemRoot createSystemRoot(String name, IClosure<? super DBSystemRoot> systemRootInit, String login, String pass, IClosure<? super SQLDataSource> dsInit) {
|
17 |
ilm |
463 |
final DBSystemRoot res;
|
61 |
ilm |
464 |
synchronized (this.getTreeMutex()) {
|
|
|
465 |
final DBSystemRoot sysRoot = this.getDBSystemRoot();
|
|
|
466 |
if (sysRoot != null) {
|
|
|
467 |
res = sysRoot;
|
83 |
ilm |
468 |
res.setDS(coalesce(systemRootInit, this.systemRootInit), login == null ? this.login : login, pass == null ? this.pass : pass, coalesce(dsInit, this.dsInit));
|
61 |
ilm |
469 |
} else {
|
83 |
ilm |
470 |
res = this.getBase(name, coalesce(systemRootInit, this.systemRootInit), login, pass, dsInit, true).getDBSystemRoot();
|
61 |
ilm |
471 |
}
|
17 |
ilm |
472 |
}
|
|
|
473 |
return res;
|
|
|
474 |
}
|
|
|
475 |
|
|
|
476 |
/**
|
|
|
477 |
* Whether the system root is created and has a datasource.
|
|
|
478 |
*
|
|
|
479 |
* @param name the system root name.
|
|
|
480 |
* @return <code>true</code> if the system root has a datasource.
|
|
|
481 |
*/
|
|
|
482 |
public final boolean isSystemRootCreated(String name) {
|
61 |
ilm |
483 |
synchronized (this.getTreeMutex()) {
|
|
|
484 |
final DBSystemRoot sysRoot = this.getDBSystemRoot();
|
|
|
485 |
if (sysRoot != null)
|
|
|
486 |
return sysRoot.hasDataSource();
|
|
|
487 |
else
|
|
|
488 |
return this.isCreated(name) && this.getBase(name).getDBSystemRoot().hasDataSource();
|
|
|
489 |
}
|
17 |
ilm |
490 |
}
|
|
|
491 |
|
67 |
ilm |
492 |
private TablesMap putBase(String baseName, SQLBase base, boolean readCache) {
|
61 |
ilm |
493 |
assert Thread.holdsLock(getTreeMutex());
|
17 |
ilm |
494 |
final CollectionChangeEventCreator c = this.createChildrenCreator();
|
|
|
495 |
this.getBases().put(baseName, base);
|
67 |
ilm |
496 |
final TablesMap res = base.init(readCache);
|
17 |
ilm |
497 |
this.fireChildrenChanged(c);
|
|
|
498 |
// if base is null, no new tables (furthermore descendantsChanged() would create our
|
|
|
499 |
// children)
|
|
|
500 |
if (base != null)
|
|
|
501 |
if (this.getDBSystemRoot() != null)
|
65 |
ilm |
502 |
this.getDBSystemRoot().descendantsChanged(this, Collections.singleton(baseName), readCache);
|
61 |
ilm |
503 |
// defaultBase must be null, otherwise the user has already expressed his choice
|
|
|
504 |
synchronized (this) {
|
|
|
505 |
final boolean setDef = this.defaultBase == null && base != null;
|
|
|
506 |
if (setDef) {
|
|
|
507 |
this.setDefaultBase(baseName);
|
|
|
508 |
}
|
17 |
ilm |
509 |
}
|
67 |
ilm |
510 |
return res;
|
17 |
ilm |
511 |
}
|
|
|
512 |
|
|
|
513 |
@Override
|
61 |
ilm |
514 |
public Map<String, SQLBase> getChildrenMap() {
|
|
|
515 |
return this.getBases().getImmutable();
|
17 |
ilm |
516 |
}
|
|
|
517 |
|
|
|
518 |
/**
|
|
|
519 |
* Has the passed base already been created. Useful as when this returns <code>true</code>,
|
|
|
520 |
* {@link #getBase(String, String, String, IClosure)} won't do anything but return the already
|
|
|
521 |
* created base, in particular the closure won't be used.
|
|
|
522 |
*
|
|
|
523 |
* @param baseName the name of the base.
|
|
|
524 |
* @return <code>true</code> if an instance of SQLBase already exists.
|
|
|
525 |
*/
|
|
|
526 |
public boolean isCreated(String baseName) {
|
61 |
ilm |
527 |
return this.getBase(baseName) != null;
|
17 |
ilm |
528 |
}
|
|
|
529 |
|
|
|
530 |
/**
|
|
|
531 |
* Met la base par défaut. Note: la première base ajoutée devient automatiquement la base par
|
|
|
532 |
* défaut.
|
|
|
533 |
*
|
|
|
534 |
* @param defaultBase le nom de la base par défaut, can be <code>null</code>.
|
|
|
535 |
* @see #getBase()
|
|
|
536 |
*/
|
|
|
537 |
public void setDefaultBase(String defaultBase) {
|
61 |
ilm |
538 |
synchronized (this.getTreeMutex()) {
|
|
|
539 |
if (defaultBase != null && !this.contains(defaultBase))
|
|
|
540 |
throw new IllegalArgumentException(defaultBase + " unknown");
|
|
|
541 |
synchronized (this) {
|
|
|
542 |
this.defaultBase = defaultBase;
|
|
|
543 |
}
|
|
|
544 |
}
|
17 |
ilm |
545 |
}
|
|
|
546 |
|
|
|
547 |
public String toString() {
|
|
|
548 |
return this.getName();
|
|
|
549 |
}
|
|
|
550 |
|
|
|
551 |
/**
|
|
|
552 |
* Return the name of the system of this server.
|
|
|
553 |
*
|
|
|
554 |
* @return the name of the system.
|
|
|
555 |
* @deprecated use {@link #getSQLSystem()}
|
|
|
556 |
*/
|
|
|
557 |
public final String getSystem() {
|
|
|
558 |
return this.getSQLSystem().getJDBCName();
|
|
|
559 |
}
|
|
|
560 |
|
|
|
561 |
public final SQLSystem getSQLSystem() {
|
|
|
562 |
return this.system;
|
|
|
563 |
}
|
|
|
564 |
|
67 |
ilm |
565 |
public final synchronized void setID(final String id) {
|
|
|
566 |
this.id = id;
|
|
|
567 |
}
|
|
|
568 |
|
|
|
569 |
// needed since the host doesn't always identify one server (e.g. 127.0.0.1:1234 might be a
|
|
|
570 |
// tunnel to different servers)
|
|
|
571 |
public final synchronized String getID() {
|
|
|
572 |
return this.id;
|
|
|
573 |
}
|
|
|
574 |
|
17 |
ilm |
575 |
public final DBFileCache getFileCache() {
|
|
|
576 |
return DBFileCache.create(this);
|
|
|
577 |
}
|
|
|
578 |
|
182 |
ilm |
579 |
/**
|
|
|
580 |
* The host name of this server.
|
|
|
581 |
*
|
|
|
582 |
* @return the host name, <code>null</code> if not accessed through network.
|
|
|
583 |
*/
|
17 |
ilm |
584 |
public final String getHostname() {
|
182 |
ilm |
585 |
return this.getHostnameAndPath().get0();
|
17 |
ilm |
586 |
}
|
182 |
ilm |
587 |
|
|
|
588 |
public final List2<String> getHostnameAndPath() {
|
|
|
589 |
return this.getSQLSystem().getHostnameAndPath(this.getName());
|
|
|
590 |
}
|
|
|
591 |
|
|
|
592 |
public final boolean isPermanent() {
|
|
|
593 |
return this.getSQLSystem().isPermanent(this.getName());
|
|
|
594 |
}
|
|
|
595 |
|
|
|
596 |
/**
|
|
|
597 |
* Whether this server runs on the local machine.
|
|
|
598 |
*
|
|
|
599 |
* @return <code>true</code> if the JVM runs on the same machine than <code>this</code>.
|
|
|
600 |
*/
|
|
|
601 |
protected final boolean isLocalhost() {
|
|
|
602 |
// this method cannot be in SQLSyntax since it is used in the constructor of SQLDataSource
|
|
|
603 |
// (which is needed by SQLSyntax.create())
|
|
|
604 |
|
|
|
605 |
final String host = this.getHostname();
|
|
|
606 |
if (host == null)
|
|
|
607 |
return true;
|
|
|
608 |
final int colonIndex = host.indexOf(':');
|
|
|
609 |
final String hostWOPort = colonIndex < 0 ? host : host.substring(0, colonIndex);
|
|
|
610 |
return NetUtils.isSelfAddr(hostWOPort);
|
|
|
611 |
}
|
|
|
612 |
|
|
|
613 |
public final boolean isPersistent() {
|
|
|
614 |
return this.getSQLSystem() != SQLSystem.H2 || !this.getName().equals(SQLSystem.H2_IN_MEMORY);
|
|
|
615 |
}
|
17 |
ilm |
616 |
}
|