17 |
ilm |
1 |
/*
|
|
|
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
|
|
|
3 |
*
|
|
|
4 |
* Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
|
|
|
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 |
package org.openconcerto.sql;
|
|
|
15 |
|
132 |
ilm |
16 |
import org.openconcerto.sql.element.SQLElementDirectory;
|
156 |
ilm |
17 |
import org.openconcerto.sql.element.SQLElementNamesFromXML;
|
132 |
ilm |
18 |
import org.openconcerto.sql.model.DBRoot;
|
|
|
19 |
import org.openconcerto.sql.model.DBStructureItem;
|
|
|
20 |
import org.openconcerto.sql.model.DBSystemRoot;
|
|
|
21 |
import org.openconcerto.sql.model.FieldMapper;
|
|
|
22 |
import org.openconcerto.sql.model.HierarchyLevel;
|
|
|
23 |
import org.openconcerto.sql.model.SQLBase;
|
|
|
24 |
import org.openconcerto.sql.model.SQLDataSource;
|
|
|
25 |
import org.openconcerto.sql.model.SQLFilter;
|
|
|
26 |
import org.openconcerto.sql.model.SQLRow;
|
|
|
27 |
import org.openconcerto.sql.model.SQLServer;
|
|
|
28 |
import org.openconcerto.sql.model.SQLSystem;
|
|
|
29 |
import org.openconcerto.sql.request.SQLFieldTranslator;
|
144 |
ilm |
30 |
import org.openconcerto.sql.users.UserManager;
|
132 |
ilm |
31 |
import org.openconcerto.sql.users.rights.UserRightsManager;
|
142 |
ilm |
32 |
import org.openconcerto.utils.BaseDirs;
|
132 |
ilm |
33 |
import org.openconcerto.utils.CollectionUtils;
|
|
|
34 |
import org.openconcerto.utils.FileUtils;
|
|
|
35 |
import org.openconcerto.utils.LogUtils;
|
|
|
36 |
import org.openconcerto.utils.MultipleOutputStream;
|
|
|
37 |
import org.openconcerto.utils.NetUtils;
|
|
|
38 |
import org.openconcerto.utils.ProductInfo;
|
|
|
39 |
import org.openconcerto.utils.RTInterruptedException;
|
156 |
ilm |
40 |
import org.openconcerto.utils.ReflectUtils;
|
132 |
ilm |
41 |
import org.openconcerto.utils.StreamUtils;
|
149 |
ilm |
42 |
import org.openconcerto.utils.Tuple2;
|
132 |
ilm |
43 |
import org.openconcerto.utils.Value;
|
|
|
44 |
import org.openconcerto.utils.cc.IClosure;
|
144 |
ilm |
45 |
import org.openconcerto.utils.cc.IPredicate;
|
132 |
ilm |
46 |
import org.openconcerto.utils.i18n.TranslationManager;
|
|
|
47 |
|
149 |
ilm |
48 |
import java.awt.Dialog.ModalityType;
|
|
|
49 |
import java.awt.event.ComponentAdapter;
|
|
|
50 |
import java.awt.event.ComponentEvent;
|
25 |
ilm |
51 |
import java.io.BufferedOutputStream;
|
17 |
ilm |
52 |
import java.io.ByteArrayOutputStream;
|
|
|
53 |
import java.io.File;
|
|
|
54 |
import java.io.FileInputStream;
|
|
|
55 |
import java.io.FileNotFoundException;
|
|
|
56 |
import java.io.FileOutputStream;
|
|
|
57 |
import java.io.IOException;
|
|
|
58 |
import java.io.InputStream;
|
25 |
ilm |
59 |
import java.io.OutputStream;
|
17 |
ilm |
60 |
import java.io.PrintStream;
|
|
|
61 |
import java.net.InetAddress;
|
|
|
62 |
import java.net.UnknownHostException;
|
|
|
63 |
import java.text.DateFormat;
|
|
|
64 |
import java.text.SimpleDateFormat;
|
|
|
65 |
import java.util.ArrayList;
|
|
|
66 |
import java.util.Arrays;
|
|
|
67 |
import java.util.Collection;
|
|
|
68 |
import java.util.Date;
|
|
|
69 |
import java.util.HashSet;
|
|
|
70 |
import java.util.List;
|
73 |
ilm |
71 |
import java.util.ListIterator;
|
|
|
72 |
import java.util.Locale;
|
177 |
ilm |
73 |
import java.util.Objects;
|
17 |
ilm |
74 |
import java.util.Properties;
|
73 |
ilm |
75 |
import java.util.ResourceBundle.Control;
|
17 |
ilm |
76 |
import java.util.Set;
|
83 |
ilm |
77 |
import java.util.concurrent.Callable;
|
|
|
78 |
import java.util.concurrent.ExecutionException;
|
|
|
79 |
import java.util.concurrent.Future;
|
|
|
80 |
import java.util.concurrent.FutureTask;
|
17 |
ilm |
81 |
import java.util.logging.FileHandler;
|
|
|
82 |
import java.util.logging.Level;
|
|
|
83 |
import java.util.logging.Logger;
|
|
|
84 |
import java.util.logging.SimpleFormatter;
|
|
|
85 |
|
149 |
ilm |
86 |
import javax.swing.JDialog;
|
|
|
87 |
import javax.swing.JFrame;
|
|
|
88 |
import javax.swing.JOptionPane;
|
|
|
89 |
import javax.swing.SwingUtilities;
|
|
|
90 |
|
21 |
ilm |
91 |
import com.jcraft.jsch.JSch;
|
|
|
92 |
import com.jcraft.jsch.Session;
|
17 |
ilm |
93 |
|
93 |
ilm |
94 |
import net.jcip.annotations.GuardedBy;
|
|
|
95 |
import net.jcip.annotations.ThreadSafe;
|
|
|
96 |
|
17 |
ilm |
97 |
/**
|
|
|
98 |
* A configuration which takes its values primarily from Properties. You should also subclass its
|
|
|
99 |
* different protected get*() methods. Used properties :
|
|
|
100 |
* <dl>
|
|
|
101 |
* <dt>server.ip</dt>
|
|
|
102 |
* <dd>ip address of the SQL server</dd>
|
|
|
103 |
* <dt>server.driver</dt>
|
|
|
104 |
* <dd>the RDBMS, see {@link org.openconcerto.sql.model.SQLDataSource#DRIVERS}</dd>
|
|
|
105 |
* <dt>server.login</dt>
|
|
|
106 |
* <dd>the login</dd>
|
|
|
107 |
* <dt>server.password</dt>
|
|
|
108 |
* <dd>the password</dd>
|
|
|
109 |
* <dt>server.base</dt>
|
|
|
110 |
* <dd>the database (only used for systems where the root level is not SQLBase)</dd>
|
|
|
111 |
* <dt>base.root</dt>
|
|
|
112 |
* <dd>the name of the DBRoot</dd>
|
|
|
113 |
* <dt>customer</dt>
|
|
|
114 |
* <dd>used to find the default base and the mapping</dd>
|
|
|
115 |
* <dt>JDBC_CONNECTION*</dt>
|
|
|
116 |
* <dd>see {@link #JDBC_CONNECTION}</dd>
|
|
|
117 |
* </dl>
|
|
|
118 |
*
|
|
|
119 |
* @author Sylvain CUAZ
|
|
|
120 |
* @see #getShowAs()
|
|
|
121 |
*/
|
83 |
ilm |
122 |
@ThreadSafe
|
17 |
ilm |
123 |
public class PropsConfiguration extends Configuration {
|
|
|
124 |
|
|
|
125 |
/**
|
|
|
126 |
* Properties prefixed with this string will be passed to the datasource as connection
|
|
|
127 |
* properties.
|
|
|
128 |
*/
|
|
|
129 |
public static final String JDBC_CONNECTION = "jdbc.connection.";
|
|
|
130 |
public static final String LOG = "log.level.";
|
|
|
131 |
/**
|
|
|
132 |
* If this system property is set to <code>true</code> then {@link #setupLogging(String)} will
|
|
|
133 |
* redirect {@link System#err} and {@link System#out}.
|
|
|
134 |
*/
|
|
|
135 |
public static final String REDIRECT_TO_FILE = "redirectToFile";
|
142 |
ilm |
136 |
/**
|
|
|
137 |
* Must be one of {@link StandardStreamsDest}.
|
|
|
138 |
*/
|
|
|
139 |
public static final String STD_STREAMS_DESTINATION = "stdStreamsDest";
|
17 |
ilm |
140 |
|
80 |
ilm |
141 |
// properties cannot contain null, so to be able to override a default, a non-null value
|
|
|
142 |
// meaning empty must be chosen (as setProperty(name, null) is the same as remove(name) i.e.
|
|
|
143 |
// get the value from the default properties)
|
|
|
144 |
public static final String EMPTY_PROP_VALUE;
|
|
|
145 |
|
17 |
ilm |
146 |
protected static enum FileMode {
|
|
|
147 |
IN_JAR, NORMAL_FILE
|
|
|
148 |
};
|
|
|
149 |
|
|
|
150 |
// eg 2009-03/26_thursday : ordered and grouped by month
|
|
|
151 |
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM/dd_EEEE");
|
|
|
152 |
|
|
|
153 |
public static String getHostname() {
|
|
|
154 |
final InetAddress addr;
|
|
|
155 |
try {
|
|
|
156 |
addr = InetAddress.getLocalHost();
|
41 |
ilm |
157 |
} catch (final UnknownHostException e) {
|
17 |
ilm |
158 |
e.printStackTrace();
|
|
|
159 |
return "local";
|
|
|
160 |
}
|
|
|
161 |
return addr.getHostName();
|
|
|
162 |
}
|
|
|
163 |
|
41 |
ilm |
164 |
protected static Properties create(final InputStream f, final Properties defaults) throws IOException {
|
|
|
165 |
final Properties props = new Properties(defaults);
|
17 |
ilm |
166 |
if (f != null) {
|
|
|
167 |
props.load(f);
|
|
|
168 |
f.close();
|
|
|
169 |
}
|
|
|
170 |
return props;
|
|
|
171 |
}
|
|
|
172 |
|
|
|
173 |
public static final Properties DEFAULTS;
|
93 |
ilm |
174 |
|
17 |
ilm |
175 |
static {
|
|
|
176 |
DEFAULTS = new Properties();
|
|
|
177 |
final File wd = new File(System.getProperty("user.dir"));
|
|
|
178 |
DEFAULTS.setProperty("wd", wd.getPath());
|
|
|
179 |
DEFAULTS.setProperty("customer", "test");
|
|
|
180 |
DEFAULTS.setProperty("server.ip", "127.0.0.1");
|
|
|
181 |
DEFAULTS.setProperty("server.login", "root");
|
80 |
ilm |
182 |
|
|
|
183 |
EMPTY_PROP_VALUE = "";
|
|
|
184 |
assert EMPTY_PROP_VALUE != null;
|
17 |
ilm |
185 |
}
|
|
|
186 |
|
|
|
187 |
private final Properties props;
|
|
|
188 |
|
|
|
189 |
// sql tree
|
83 |
ilm |
190 |
@GuardedBy("treeLock")
|
17 |
ilm |
191 |
private SQLServer server;
|
83 |
ilm |
192 |
@GuardedBy("treeLock")
|
17 |
ilm |
193 |
private DBSystemRoot sysRoot;
|
83 |
ilm |
194 |
@GuardedBy("treeLock")
|
17 |
ilm |
195 |
private DBRoot root;
|
83 |
ilm |
196 |
// created from root
|
|
|
197 |
@GuardedBy("treeLock")
|
144 |
ilm |
198 |
private UserManager uMngr;
|
|
|
199 |
@GuardedBy("treeLock")
|
73 |
ilm |
200 |
private UserRightsManager urMngr;
|
17 |
ilm |
201 |
// rest
|
83 |
ilm |
202 |
@GuardedBy("restLock")
|
41 |
ilm |
203 |
private ProductInfo productInfo;
|
83 |
ilm |
204 |
@GuardedBy("restLock")
|
17 |
ilm |
205 |
private SQLFilter filter;
|
83 |
ilm |
206 |
private final Addable<SQLElementDirectory> directory;
|
|
|
207 |
@GuardedBy("restLock")
|
17 |
ilm |
208 |
private File wd;
|
83 |
ilm |
209 |
@GuardedBy("restLock")
|
142 |
ilm |
210 |
private BaseDirs baseDirs;
|
|
|
211 |
@GuardedBy("restLock")
|
41 |
ilm |
212 |
private File logDir;
|
177 |
ilm |
213 |
@GuardedBy("restLock")
|
|
|
214 |
private Locale locale = Locale.getDefault();
|
142 |
ilm |
215 |
private final boolean inIDE;
|
|
|
216 |
|
17 |
ilm |
217 |
// split sql tree and the rest since creating the tree is costly
|
|
|
218 |
// and nodes are inter-dependant, while the rest is mostly fast
|
|
|
219 |
// different instances, otherwise lock every Conf instances
|
|
|
220 |
private final Object treeLock = new String("treeLock");
|
|
|
221 |
private final Object restLock = new String("everythingElseLock");
|
|
|
222 |
|
|
|
223 |
// SSL
|
83 |
ilm |
224 |
@GuardedBy("treeLock")
|
21 |
ilm |
225 |
private Session conn;
|
83 |
ilm |
226 |
@GuardedBy("treeLock")
|
149 |
ilm |
227 |
private int tunnelLocalPort = -1;
|
|
|
228 |
@GuardedBy("treeLock")
|
|
|
229 |
private Thread sslThread;
|
83 |
ilm |
230 |
|
73 |
ilm |
231 |
private FieldMapper fieldMapper;
|
17 |
ilm |
232 |
|
83 |
ilm |
233 |
@GuardedBy("treeLock")
|
|
|
234 |
private boolean destroyed;
|
|
|
235 |
|
17 |
ilm |
236 |
public PropsConfiguration() throws IOException {
|
|
|
237 |
this(new File("fwk_SQL.properties"), DEFAULTS);
|
|
|
238 |
}
|
|
|
239 |
|
|
|
240 |
/**
|
|
|
241 |
* Creates a new setup.
|
|
|
242 |
*
|
|
|
243 |
* @param f the file from which to load.
|
|
|
244 |
* @param defaults the defaults, can be <code>null</code>.
|
|
|
245 |
* @throws IOException if an error occurs while reading f.
|
|
|
246 |
*/
|
41 |
ilm |
247 |
public PropsConfiguration(final File f, final Properties defaults) throws IOException {
|
17 |
ilm |
248 |
this(new FileInputStream(f), defaults);
|
|
|
249 |
}
|
|
|
250 |
|
41 |
ilm |
251 |
public PropsConfiguration(final InputStream f, final Properties defaults) throws IOException {
|
17 |
ilm |
252 |
this(create(f, defaults));
|
|
|
253 |
}
|
|
|
254 |
|
41 |
ilm |
255 |
public PropsConfiguration(final Properties props) {
|
17 |
ilm |
256 |
this.props = props;
|
83 |
ilm |
257 |
// SQLElementDirectory is thread-safe
|
|
|
258 |
this.directory = new Addable<SQLElementDirectory>() {
|
|
|
259 |
@Override
|
|
|
260 |
protected SQLElementDirectory create() {
|
142 |
ilm |
261 |
final SQLElementDirectory res = createDirectory();
|
|
|
262 |
res.setTranslator(createTranslator(res));
|
|
|
263 |
return res;
|
83 |
ilm |
264 |
}
|
|
|
265 |
|
|
|
266 |
@Override
|
|
|
267 |
protected void add(SQLElementDirectory obj, Configuration conf) {
|
|
|
268 |
obj.putAll(conf.getDirectory());
|
|
|
269 |
}
|
144 |
ilm |
270 |
|
|
|
271 |
@Override
|
|
|
272 |
protected void destroy(Future<SQLElementDirectory> future) {
|
|
|
273 |
super.destroy(future);
|
|
|
274 |
try {
|
|
|
275 |
future.get().destroy();
|
|
|
276 |
} catch (Exception e) {
|
|
|
277 |
throw new IllegalStateException("Couldn't destroy the directory", e);
|
|
|
278 |
}
|
|
|
279 |
}
|
83 |
ilm |
280 |
};
|
142 |
ilm |
281 |
this.inIDE = Boolean.getBoolean("inIDE");
|
17 |
ilm |
282 |
this.setUp();
|
|
|
283 |
}
|
|
|
284 |
|
|
|
285 |
@Override
|
|
|
286 |
public void destroy() {
|
83 |
ilm |
287 |
synchronized (this.treeLock) {
|
|
|
288 |
if (this.destroyed)
|
|
|
289 |
return;
|
|
|
290 |
this.destroyed = true;
|
|
|
291 |
if (this.server != null) {
|
|
|
292 |
this.server.destroy();
|
|
|
293 |
}
|
|
|
294 |
closeSSLConnection();
|
144 |
ilm |
295 |
if (this.uMngr != null) {
|
|
|
296 |
UserManager.getSingletonManager().clearInstanceIfSame(this.uMngr);
|
|
|
297 |
this.uMngr.destroy();
|
|
|
298 |
}
|
|
|
299 |
if (this.urMngr != null) {
|
|
|
300 |
UserRightsManager.getSingletonManager().clearInstanceIfSame(this.urMngr);
|
|
|
301 |
this.urMngr.destroy();
|
|
|
302 |
}
|
17 |
ilm |
303 |
}
|
83 |
ilm |
304 |
|
|
|
305 |
this.directory.destroy();
|
|
|
306 |
super.destroy();
|
17 |
ilm |
307 |
}
|
|
|
308 |
|
83 |
ilm |
309 |
public final boolean isDestroyed() {
|
|
|
310 |
synchronized (this.treeLock) {
|
|
|
311 |
return this.destroyed;
|
|
|
312 |
}
|
|
|
313 |
}
|
|
|
314 |
|
|
|
315 |
private final void checkDestroyed() {
|
|
|
316 |
checkDestroyed(this.isDestroyed());
|
|
|
317 |
}
|
|
|
318 |
|
|
|
319 |
static private final void checkDestroyed(final boolean d) {
|
|
|
320 |
if (d)
|
|
|
321 |
throw new IllegalStateException("Destroyed");
|
|
|
322 |
}
|
|
|
323 |
|
142 |
ilm |
324 |
public final boolean isInIDE() {
|
|
|
325 |
return this.inIDE;
|
|
|
326 |
}
|
|
|
327 |
|
41 |
ilm |
328 |
public final String getProperty(final String name) {
|
17 |
ilm |
329 |
return this.props.getProperty(name);
|
|
|
330 |
}
|
|
|
331 |
|
41 |
ilm |
332 |
public final String getProperty(final String name, final String def) {
|
17 |
ilm |
333 |
return this.props.getProperty(name, def);
|
|
|
334 |
}
|
|
|
335 |
|
|
|
336 |
// since null aren't allowed, null means remove
|
41 |
ilm |
337 |
protected final void setProperty(final String name, final String val) {
|
17 |
ilm |
338 |
if (val == null)
|
|
|
339 |
this.props.remove(name);
|
|
|
340 |
else
|
|
|
341 |
this.props.setProperty(name, val);
|
|
|
342 |
}
|
|
|
343 |
|
41 |
ilm |
344 |
protected final void setProductInfo(final ProductInfo productInfo) {
|
83 |
ilm |
345 |
synchronized (this.restLock) {
|
|
|
346 |
this.productInfo = productInfo;
|
|
|
347 |
}
|
41 |
ilm |
348 |
}
|
|
|
349 |
|
17 |
ilm |
350 |
private void setUp() {
|
83 |
ilm |
351 |
synchronized (this.treeLock) {
|
|
|
352 |
this.destroyed = false;
|
|
|
353 |
this.server = null;
|
|
|
354 |
this.sysRoot = null;
|
|
|
355 |
this.root = null;
|
|
|
356 |
}
|
|
|
357 |
synchronized (this.restLock) {
|
|
|
358 |
this.setProductInfo(ProductInfo.getInstance());
|
|
|
359 |
this.setFilter(null);
|
|
|
360 |
}
|
17 |
ilm |
361 |
}
|
|
|
362 |
|
65 |
ilm |
363 |
public final SQLSystem getSystem() {
|
|
|
364 |
return SQLSystem.get(this.getProperty("server.driver"));
|
17 |
ilm |
365 |
}
|
|
|
366 |
|
|
|
367 |
protected String getLogin() {
|
|
|
368 |
return this.getProperty("server.login");
|
|
|
369 |
}
|
|
|
370 |
|
|
|
371 |
protected String getPassword() {
|
|
|
372 |
return this.getProperty("server.password");
|
|
|
373 |
}
|
|
|
374 |
|
|
|
375 |
public String getDefaultBase() {
|
65 |
ilm |
376 |
final boolean rootIsBase = this.getSystem().getDBRootLevel().equals(HierarchyLevel.SQLBASE);
|
17 |
ilm |
377 |
return rootIsBase ? this.getRootName() : this.getSystemRootName();
|
|
|
378 |
}
|
|
|
379 |
|
156 |
ilm |
380 |
private final String toClassName(final String rsrcName) {
|
|
|
381 |
if (rsrcName.charAt(0) == '/')
|
|
|
382 |
return rsrcName.substring(1).replace('/', '.');
|
|
|
383 |
else
|
|
|
384 |
return this.getResourceWD().getPackage().getName() + '.' + rsrcName.replace('/', '.');
|
|
|
385 |
}
|
|
|
386 |
|
17 |
ilm |
387 |
/**
|
|
|
388 |
* Return the correct stream depending on file mode. If file mode is
|
|
|
389 |
* {@link FileMode#NORMAL_FILE} it will first check if a file named <code>name</code> exists,
|
|
|
390 |
* otherwise it will look in the jar.
|
|
|
391 |
*
|
|
|
392 |
* @param name name of the stream, eg /ilm/f.xml.
|
|
|
393 |
* @return the corresponding stream, or <code>null</code> if not found.
|
|
|
394 |
*/
|
41 |
ilm |
395 |
public final InputStream getStream(final String name) {
|
17 |
ilm |
396 |
final File f = getFile(name);
|
|
|
397 |
if (mustUseClassloader(f)) {
|
156 |
ilm |
398 |
return getResourceWD().getResourceAsStream(name);
|
17 |
ilm |
399 |
} else
|
|
|
400 |
try {
|
|
|
401 |
return new FileInputStream(f);
|
41 |
ilm |
402 |
} catch (final FileNotFoundException e) {
|
17 |
ilm |
403 |
return null;
|
|
|
404 |
}
|
|
|
405 |
}
|
|
|
406 |
|
156 |
ilm |
407 |
// the "working directory" where relative names are resolved
|
|
|
408 |
protected Class<? extends PropsConfiguration> getResourceWD() {
|
|
|
409 |
return this.getClass();
|
|
|
410 |
}
|
|
|
411 |
|
41 |
ilm |
412 |
private File getFile(final String name) {
|
17 |
ilm |
413 |
return new File(name.startsWith("/") ? name.substring(1) : name);
|
|
|
414 |
}
|
|
|
415 |
|
|
|
416 |
private boolean mustUseClassloader(final File f) {
|
|
|
417 |
return this.getFileMode() == FileMode.IN_JAR || !f.exists();
|
|
|
418 |
}
|
|
|
419 |
|
41 |
ilm |
420 |
public final String getResource(final String name) {
|
17 |
ilm |
421 |
final File f = getFile(name);
|
|
|
422 |
if (mustUseClassloader(f)) {
|
156 |
ilm |
423 |
return this.getResourceWD().getResource(name).toExternalForm();
|
17 |
ilm |
424 |
} else {
|
|
|
425 |
return f.getAbsolutePath();
|
|
|
426 |
}
|
|
|
427 |
}
|
|
|
428 |
|
|
|
429 |
protected FileMode getFileMode() {
|
|
|
430 |
return FileMode.IN_JAR;
|
|
|
431 |
}
|
|
|
432 |
|
|
|
433 |
protected final DBRoot createRoot() {
|
80 |
ilm |
434 |
final Value<String> rootName = getRootNameValue();
|
|
|
435 |
if (rootName.hasValue())
|
|
|
436 |
return this.getSystemRoot().getRoot(rootName.getValue());
|
17 |
ilm |
437 |
else
|
|
|
438 |
throw new NullPointerException("no rootname");
|
|
|
439 |
}
|
|
|
440 |
|
73 |
ilm |
441 |
// return null, if none desired
|
|
|
442 |
protected UserRightsManager createUserRightsManager(final DBRoot root) {
|
144 |
ilm |
443 |
return UserRightsManager.getSingletonManager().setInstanceIfNone(root);
|
73 |
ilm |
444 |
}
|
|
|
445 |
|
17 |
ilm |
446 |
public String getRootName() {
|
80 |
ilm |
447 |
return this.getProperty("base.root", EMPTY_PROP_VALUE);
|
17 |
ilm |
448 |
}
|
|
|
449 |
|
80 |
ilm |
450 |
public final Value<String> getRootNameValue() {
|
|
|
451 |
final String res = getRootName();
|
|
|
452 |
return res == null || EMPTY_PROP_VALUE.equals(res) ? Value.<String> getNone() : Value.getSome(res);
|
|
|
453 |
}
|
|
|
454 |
|
17 |
ilm |
455 |
protected SQLFilter createFilter() {
|
|
|
456 |
return SQLFilter.create(this.getSystemRoot(), getDirectory());
|
|
|
457 |
}
|
|
|
458 |
|
|
|
459 |
public String getWanHostAndPort() {
|
|
|
460 |
final String wanAddr = getProperty("server.wan.addr");
|
|
|
461 |
final String wanPort = getProperty("server.wan.port", "22");
|
|
|
462 |
return wanAddr + ":" + wanPort;
|
|
|
463 |
}
|
|
|
464 |
|
67 |
ilm |
465 |
public final boolean isUsingSSH() {
|
83 |
ilm |
466 |
synchronized (this.treeLock) {
|
149 |
ilm |
467 |
return this.sslThread != null;
|
83 |
ilm |
468 |
}
|
17 |
ilm |
469 |
}
|
|
|
470 |
|
149 |
ilm |
471 |
public final int getTunnelLocalPort() {
|
|
|
472 |
synchronized (this.treeLock) {
|
|
|
473 |
return this.tunnelLocalPort;
|
|
|
474 |
}
|
|
|
475 |
}
|
|
|
476 |
|
67 |
ilm |
477 |
public final boolean hasWANProperties() {
|
|
|
478 |
final String wanAddr = getProperty("server.wan.addr");
|
|
|
479 |
final String wanPort = getProperty("server.wan.port");
|
|
|
480 |
return hasWANProperties(wanAddr, wanPort);
|
|
|
481 |
}
|
|
|
482 |
|
|
|
483 |
private final boolean hasWANProperties(String wanAddr, String wanPort) {
|
|
|
484 |
return wanAddr != null && wanPort != null;
|
|
|
485 |
}
|
|
|
486 |
|
17 |
ilm |
487 |
protected SQLServer createServer() {
|
|
|
488 |
final String wanAddr = getProperty("server.wan.addr");
|
|
|
489 |
final String wanPort = getProperty("server.wan.port");
|
67 |
ilm |
490 |
if (!hasWANProperties(wanAddr, wanPort))
|
17 |
ilm |
491 |
return doCreateServer();
|
|
|
492 |
|
149 |
ilm |
493 |
final Tuple2<String, Integer> serverAndPort = parseServerAddressAndPort();
|
|
|
494 |
final String serverAndPortString = serverAndPort.get0() + ":" + serverAndPort.get1();
|
142 |
ilm |
495 |
|
67 |
ilm |
496 |
// if wanAddr is specified, always include it in ID, that way if we connect through the LAN
|
|
|
497 |
// or through the WAN we have the same ID
|
149 |
ilm |
498 |
final String serverID = "tunnel to " + wanAddr + ":" + wanPort + " then " + serverAndPortString;
|
17 |
ilm |
499 |
final Logger log = Log.get();
|
|
|
500 |
Exception origExn = null;
|
|
|
501 |
if (!"true".equals(getProperty("server.wan.only"))) {
|
|
|
502 |
try {
|
149 |
ilm |
503 |
final SQLServer defaultServer = doCreateServer(serverAndPortString, null, serverID);
|
17 |
ilm |
504 |
// works since all ds params are provided by doCreateServer()
|
|
|
505 |
defaultServer.getSystemRoot(getSystemRootName());
|
|
|
506 |
// ok
|
|
|
507 |
log.config("using " + defaultServer);
|
|
|
508 |
|
|
|
509 |
return defaultServer;
|
41 |
ilm |
510 |
} catch (final RuntimeException e) {
|
17 |
ilm |
511 |
origExn = e;
|
|
|
512 |
// on essaye par SSL
|
|
|
513 |
log.config(e.getLocalizedMessage());
|
|
|
514 |
}
|
|
|
515 |
assert origExn != null;
|
|
|
516 |
}
|
149 |
ilm |
517 |
final SQLServer serverThruSSL;
|
17 |
ilm |
518 |
try {
|
149 |
ilm |
519 |
log.info("Connecting with SSL to " + wanAddr + ":" + wanPort);
|
|
|
520 |
this.openSSLConnection(wanAddr, Integer.valueOf(wanPort), serverAndPort.get0(), serverAndPort.get1());
|
|
|
521 |
serverThruSSL = doCreateServer("localhost:" + this.tunnelLocalPort, null, serverID);
|
17 |
ilm |
522 |
serverThruSSL.getSystemRoot(getSystemRootName());
|
41 |
ilm |
523 |
} catch (final Exception e) {
|
149 |
ilm |
524 |
// even if the tunnel was set up successfully, close it if the SQLServer couldn't be
|
|
|
525 |
// created, that way the next time createServer() is called, we can start again the
|
|
|
526 |
// whole process (e.g. checking properties, retrying non-WAN server) without having to
|
|
|
527 |
// worry about a lingering tunnel.
|
17 |
ilm |
528 |
this.closeSSLConnection();
|
149 |
ilm |
529 |
// no datasource will remain that uses the port, so forget about it and find a new one
|
|
|
530 |
// the next time
|
|
|
531 |
this.tunnelLocalPort = -1;
|
|
|
532 |
final IllegalStateException exn = new IllegalStateException("Couldn't connect to the DB through SSL", e);
|
|
|
533 |
if (origExn != null)
|
|
|
534 |
exn.addSuppressed(origExn);
|
|
|
535 |
throw exn;
|
17 |
ilm |
536 |
}
|
|
|
537 |
return serverThruSSL;
|
|
|
538 |
|
|
|
539 |
}
|
|
|
540 |
|
149 |
ilm |
541 |
// TODO add and use server.port
|
|
|
542 |
public final Tuple2<String, Integer> parseServerAddressAndPort() {
|
|
|
543 |
final String serverPropVal = getProperty("server.ip");
|
|
|
544 |
final List<String> serverAndPort = Arrays.asList(serverPropVal.split(":"));
|
|
|
545 |
if (serverAndPort.size() != 2)
|
|
|
546 |
throw new IllegalStateException("Not in 'host:port' format : " + serverPropVal);
|
|
|
547 |
return Tuple2.create(serverAndPort.get(0), Integer.valueOf(serverAndPort.get(1)));
|
|
|
548 |
}
|
|
|
549 |
|
|
|
550 |
protected final void reconnectSSL() throws Exception {
|
|
|
551 |
synchronized (this.treeLock) {
|
|
|
552 |
// already destroyed or still OK
|
|
|
553 |
if (this.conn == null || this.conn.isConnected())
|
|
|
554 |
return;
|
|
|
555 |
|
|
|
556 |
Log.get().log(Level.WARNING, "SSL disconnected, trying to reconnect");
|
|
|
557 |
|
|
|
558 |
final String wanAddr = getProperty("server.wan.addr");
|
|
|
559 |
final String wanPort = getProperty("server.wan.port");
|
|
|
560 |
final Tuple2<String, Integer> serverAndPort = parseServerAddressAndPort();
|
|
|
561 |
|
|
|
562 |
try {
|
|
|
563 |
// cannot just call connect() again, as Session is one-time use
|
|
|
564 |
// http://flyingjxswithjava.blogspot.fr/2015/03/comjcraftjschjschexception-packet.html
|
|
|
565 |
this.openSSLConnection(wanAddr, Integer.valueOf(wanPort), serverAndPort.get0(), serverAndPort.get1());
|
|
|
566 |
Log.get().log(Level.WARNING, "SSL successfully reconnected to " + this.conn.getHost() + ":" + this.conn.getPort());
|
|
|
567 |
} catch (Exception e) {
|
|
|
568 |
// don't leave an SSL connection without a tunnel
|
|
|
569 |
this.conn.disconnect();
|
|
|
570 |
throw e;
|
|
|
571 |
}
|
|
|
572 |
}
|
|
|
573 |
}
|
|
|
574 |
|
17 |
ilm |
575 |
private SQLServer doCreateServer() {
|
67 |
ilm |
576 |
return doCreateServer(null);
|
|
|
577 |
}
|
|
|
578 |
|
|
|
579 |
private SQLServer doCreateServer(final String id) {
|
|
|
580 |
return doCreateServer(this.getProperty("server.ip"), null, id);
|
|
|
581 |
}
|
|
|
582 |
|
|
|
583 |
private SQLServer doCreateServer(final String host, final String port, final String id) {
|
17 |
ilm |
584 |
// give login/password as its often the case that they are the same for all the bases of a
|
|
|
585 |
// server (mandated for MySQL : when the graph is built, it needs access to all the bases)
|
67 |
ilm |
586 |
final SQLServer res = new SQLServer(getSystem(), host, port, getLogin(), getPassword(), new IClosure<DBSystemRoot>() {
|
17 |
ilm |
587 |
@Override
|
41 |
ilm |
588 |
public void executeChecked(final DBSystemRoot input) {
|
65 |
ilm |
589 |
input.setRootsToMap(getRootsToMap());
|
63 |
ilm |
590 |
initSystemRoot(input);
|
17 |
ilm |
591 |
}
|
|
|
592 |
}, new IClosure<SQLDataSource>() {
|
|
|
593 |
@Override
|
41 |
ilm |
594 |
public void executeChecked(final SQLDataSource input) {
|
17 |
ilm |
595 |
initDS(input);
|
|
|
596 |
}
|
|
|
597 |
});
|
67 |
ilm |
598 |
if (id != null)
|
|
|
599 |
res.setID(id);
|
|
|
600 |
return res;
|
17 |
ilm |
601 |
}
|
|
|
602 |
|
149 |
ilm |
603 |
private void openSSLConnection(final String addr, final int port, final String tunnelRemoteHost, final int tunnelRemotePort) {
|
83 |
ilm |
604 |
checkDestroyed();
|
17 |
ilm |
605 |
final String username = getSSLUserName();
|
41 |
ilm |
606 |
final String pass = getSSLPassword();
|
149 |
ilm |
607 |
final boolean reconnect = this.tunnelLocalPort > 0;
|
17 |
ilm |
608 |
boolean isAuthenticated = false;
|
21 |
ilm |
609 |
|
|
|
610 |
final JSch jsch = new JSch();
|
17 |
ilm |
611 |
try {
|
41 |
ilm |
612 |
if (pass == null) {
|
|
|
613 |
final ByteArrayOutputStream out = new ByteArrayOutputStream(700);
|
|
|
614 |
final String name = username + "_dsa";
|
|
|
615 |
final InputStream in = getClass().getResourceAsStream(name);
|
|
|
616 |
if (in == null)
|
|
|
617 |
throw new IllegalStateException("Missing private key " + getClass().getCanonicalName() + "/" + name);
|
|
|
618 |
StreamUtils.copy(in, out);
|
|
|
619 |
in.close();
|
|
|
620 |
jsch.addIdentity(username, out.toByteArray(), null, null);
|
|
|
621 |
}
|
21 |
ilm |
622 |
|
|
|
623 |
this.conn = jsch.getSession(username, addr, port);
|
41 |
ilm |
624 |
if (pass != null)
|
|
|
625 |
this.conn.setPassword(pass);
|
21 |
ilm |
626 |
final Properties config = new Properties();
|
|
|
627 |
// Set StrictHostKeyChecking property to no to avoid UnknownHostKey issue
|
|
|
628 |
config.put("StrictHostKeyChecking", "no");
|
|
|
629 |
// *2 gain
|
|
|
630 |
config.put("compression.s2c", "zlib@openssh.com,zlib,none");
|
|
|
631 |
config.put("compression.c2s", "zlib@openssh.com,zlib,none");
|
|
|
632 |
this.conn.setConfig(config);
|
|
|
633 |
// wait no more than 6 seconds for TCP connection
|
149 |
ilm |
634 |
this.conn.setTimeout(6000);
|
|
|
635 |
// ATTN for now this just calls setTimeout()
|
|
|
636 |
this.conn.setServerAliveInterval(6000);
|
|
|
637 |
this.conn.setServerAliveCountMax(1);
|
|
|
638 |
this.conn.connect();
|
|
|
639 |
|
73 |
ilm |
640 |
afterSSLConnect(this.conn);
|
21 |
ilm |
641 |
|
|
|
642 |
isAuthenticated = true;
|
149 |
ilm |
643 |
|
|
|
644 |
// keep same local port so that we don't have to change the datasource
|
|
|
645 |
final int localPort = reconnect ? this.tunnelLocalPort : NetUtils.findFreePort(5436);
|
|
|
646 |
try {
|
|
|
647 |
Log.get().info("Creating SSL tunnel from local port " + localPort + " to remote " + tunnelRemoteHost + ":" + tunnelRemotePort);
|
|
|
648 |
this.conn.setPortForwardingL(localPort, tunnelRemoteHost, tunnelRemotePort);
|
|
|
649 |
} catch (final Exception e1) {
|
|
|
650 |
throw new IllegalStateException("Impossible de créer le tunnel sécurisé", e1);
|
|
|
651 |
}
|
|
|
652 |
assert reconnect == (this.sslThread != null);
|
|
|
653 |
if (!reconnect) {
|
|
|
654 |
this.tunnelLocalPort = localPort;
|
|
|
655 |
// With ServerAliveInterval & ServerAliveCount, the connection should disconnect
|
|
|
656 |
// itself in case of network failure, so we try to reconnect it
|
|
|
657 |
final Thread t = new Thread(new Runnable() {
|
|
|
658 |
@Override
|
|
|
659 |
public void run() {
|
|
|
660 |
while (true) {
|
|
|
661 |
try {
|
|
|
662 |
Thread.sleep(2000);
|
|
|
663 |
try {
|
|
|
664 |
reconnectSSL();
|
|
|
665 |
} catch (Exception e) {
|
|
|
666 |
// re-try later
|
|
|
667 |
Log.get().log(Level.WARNING, "Error while checking SSL connection", e);
|
|
|
668 |
}
|
|
|
669 |
} catch (InterruptedException e) {
|
|
|
670 |
// used by destroy()
|
|
|
671 |
break;
|
|
|
672 |
}
|
|
|
673 |
}
|
|
|
674 |
}
|
|
|
675 |
});
|
|
|
676 |
t.setDaemon(true);
|
|
|
677 |
t.setName("SSL connection watcher");
|
|
|
678 |
t.start();
|
|
|
679 |
this.sslThread = t;
|
|
|
680 |
}
|
|
|
681 |
assert this.sslThread != null;
|
41 |
ilm |
682 |
} catch (final Exception e) {
|
17 |
ilm |
683 |
throw new IllegalStateException("Connection failed", e);
|
|
|
684 |
}
|
|
|
685 |
if (!isAuthenticated)
|
|
|
686 |
throw new IllegalStateException("Authentication failed.");
|
|
|
687 |
}
|
|
|
688 |
|
73 |
ilm |
689 |
protected void afterSSLConnect(Session conn) {
|
|
|
690 |
}
|
|
|
691 |
|
17 |
ilm |
692 |
public String getSSLUserName() {
|
|
|
693 |
return this.getProperty("server.wan.user");
|
|
|
694 |
}
|
|
|
695 |
|
41 |
ilm |
696 |
protected String getSSLPassword() {
|
|
|
697 |
return this.getProperty("server.wan.password");
|
|
|
698 |
}
|
|
|
699 |
|
17 |
ilm |
700 |
private void closeSSLConnection() {
|
83 |
ilm |
701 |
synchronized (this.treeLock) {
|
149 |
ilm |
702 |
if (this.sslThread != null) {
|
|
|
703 |
this.sslThread.interrupt();
|
|
|
704 |
this.sslThread = null;
|
|
|
705 |
}
|
83 |
ilm |
706 |
if (this.conn != null) {
|
|
|
707 |
this.conn.disconnect();
|
|
|
708 |
this.conn = null;
|
|
|
709 |
}
|
17 |
ilm |
710 |
}
|
|
|
711 |
}
|
|
|
712 |
|
67 |
ilm |
713 |
// the result can be modified (avoid that each subclass recreates an instance)
|
83 |
ilm |
714 |
// but it can be null (meaning map all)
|
17 |
ilm |
715 |
protected Collection<String> getRootsToMap() {
|
83 |
ilm |
716 |
final String rootsToMap = getProperty("systemRoot.rootsToMap");
|
|
|
717 |
if ("*".equals(rootsToMap))
|
|
|
718 |
return null;
|
|
|
719 |
|
17 |
ilm |
720 |
final Set<String> res = new HashSet<String>();
|
|
|
721 |
|
80 |
ilm |
722 |
final Value<String> rootName = getRootNameValue();
|
|
|
723 |
if (rootName.hasValue())
|
|
|
724 |
res.add(rootName.getValue());
|
17 |
ilm |
725 |
if (rootsToMap != null)
|
|
|
726 |
res.addAll(SQLRow.toList(rootsToMap));
|
|
|
727 |
|
|
|
728 |
return res;
|
|
|
729 |
}
|
|
|
730 |
|
67 |
ilm |
731 |
// the result can be modified (avoid that each subclass recreates an instance)
|
|
|
732 |
protected List<String> getRootPath() {
|
|
|
733 |
return new ArrayList<String>(SQLRow.toList(getProperty("systemRoot.rootPath", "")));
|
|
|
734 |
}
|
|
|
735 |
|
17 |
ilm |
736 |
public String getSystemRootName() {
|
|
|
737 |
return this.getProperty("systemRoot");
|
|
|
738 |
}
|
|
|
739 |
|
|
|
740 |
protected DBSystemRoot createSystemRoot() {
|
|
|
741 |
// all ds params specified by createServer()
|
|
|
742 |
final DBSystemRoot res = this.getServer(false).getSystemRoot(this.getSystemRootName());
|
83 |
ilm |
743 |
setupSystemRoot(res, true);
|
|
|
744 |
return res;
|
|
|
745 |
}
|
|
|
746 |
|
|
|
747 |
// to be called after having a data source
|
|
|
748 |
protected final void setupSystemRoot(final DBSystemRoot res) {
|
|
|
749 |
this.setupSystemRoot(res, false);
|
|
|
750 |
}
|
|
|
751 |
|
|
|
752 |
private void setupSystemRoot(final DBSystemRoot res, final boolean brandNew) {
|
|
|
753 |
if (!brandNew)
|
|
|
754 |
res.unsetRootPath();
|
17 |
ilm |
755 |
// handle case when the root is not yet created
|
|
|
756 |
if (res.getChildrenNames().contains(this.getRootName()))
|
|
|
757 |
res.setDefaultRoot(this.getRootName());
|
67 |
ilm |
758 |
for (final String root : getRootPath()) {
|
|
|
759 |
// not all the items of the path may exist in every databases (eg Controle.Common)
|
|
|
760 |
if (res.getChildrenNames().contains(root))
|
|
|
761 |
res.appendToRootPath(root);
|
17 |
ilm |
762 |
}
|
|
|
763 |
}
|
|
|
764 |
|
63 |
ilm |
765 |
// called at the end of the DBSystemRoot constructor (before having a data source)
|
|
|
766 |
protected void initSystemRoot(DBSystemRoot input) {
|
|
|
767 |
}
|
|
|
768 |
|
17 |
ilm |
769 |
protected void initDS(final SQLDataSource ds) {
|
|
|
770 |
ds.setCacheEnabled(true);
|
67 |
ilm |
771 |
// supported by postgreSQL from 9.1-901, see also Connection#setClientInfo
|
83 |
ilm |
772 |
// also supported by MS SQL
|
73 |
ilm |
773 |
final String appID = getAppID();
|
|
|
774 |
if (appID != null)
|
|
|
775 |
ds.addConnectionProperty("ApplicationName", appID);
|
17 |
ilm |
776 |
propIterate(new IClosure<String>() {
|
41 |
ilm |
777 |
@Override
|
|
|
778 |
public void executeChecked(final String propName) {
|
17 |
ilm |
779 |
final String jdbcName = propName.substring(JDBC_CONNECTION.length());
|
|
|
780 |
ds.addConnectionProperty(jdbcName, PropsConfiguration.this.getProperty(propName));
|
|
|
781 |
}
|
|
|
782 |
}, JDBC_CONNECTION);
|
|
|
783 |
}
|
|
|
784 |
|
|
|
785 |
public final void propIterate(final IClosure<String> cl, final String startsWith) {
|
144 |
ilm |
786 |
this.propIterate(cl, new IPredicate<String>() {
|
41 |
ilm |
787 |
@Override
|
144 |
ilm |
788 |
public boolean evaluateChecked(final String propName) {
|
|
|
789 |
return propName.startsWith(startsWith);
|
17 |
ilm |
790 |
}
|
|
|
791 |
});
|
|
|
792 |
}
|
|
|
793 |
|
|
|
794 |
/**
|
|
|
795 |
* Apply <code>cl</code> for each property that matches <code>filter</code>.
|
|
|
796 |
*
|
|
|
797 |
* @param cl what to do for each found property.
|
|
|
798 |
* @param filter which property to use.
|
|
|
799 |
*/
|
144 |
ilm |
800 |
public final void propIterate(final IClosure<String> cl, final IPredicate<? super String> filter) {
|
41 |
ilm |
801 |
for (final String propName : this.props.stringPropertyNames()) {
|
144 |
ilm |
802 |
if (filter.evaluateChecked(propName)) {
|
17 |
ilm |
803 |
cl.executeChecked(propName);
|
|
|
804 |
}
|
|
|
805 |
}
|
|
|
806 |
}
|
|
|
807 |
|
151 |
ilm |
808 |
public final Set<String> getPropertyNames() {
|
|
|
809 |
return this.props.stringPropertyNames();
|
|
|
810 |
}
|
|
|
811 |
|
17 |
ilm |
812 |
/**
|
|
|
813 |
* For each property starting with {@link #LOG}, set the level of the specified logger to the
|
|
|
814 |
* property's value. Eg if there's "log.level.=FINE", the root logger will be set to log FINE
|
|
|
815 |
* messages.
|
|
|
816 |
*/
|
|
|
817 |
public final void setLoggersLevel() {
|
|
|
818 |
this.propIterate(new IClosure<String>() {
|
41 |
ilm |
819 |
@Override
|
|
|
820 |
public void executeChecked(final String propName) {
|
17 |
ilm |
821 |
final String logName = propName.substring(LOG.length());
|
|
|
822 |
LogUtils.getLogger(logName).setLevel(Level.parse(getProperty(propName)));
|
|
|
823 |
}
|
|
|
824 |
}, LOG);
|
|
|
825 |
}
|
|
|
826 |
|
|
|
827 |
public void setupLogging() {
|
|
|
828 |
this.setupLogging("logs");
|
|
|
829 |
}
|
|
|
830 |
|
142 |
ilm |
831 |
public final void setupLogging(final String dirName) {
|
|
|
832 |
this.setupLogging(dirName, getStandardStreamsDestination());
|
17 |
ilm |
833 |
}
|
|
|
834 |
|
142 |
ilm |
835 |
protected final StandardStreamsDest getStandardStreamsDestination() {
|
|
|
836 |
String propVal = System.getProperty(STD_STREAMS_DESTINATION);
|
|
|
837 |
if (propVal == null)
|
|
|
838 |
propVal = this.getProperty(STD_STREAMS_DESTINATION);
|
|
|
839 |
|
|
|
840 |
final StandardStreamsDest res;
|
|
|
841 |
if (propVal != null)
|
|
|
842 |
res = StandardStreamsDest.valueOf(propVal);
|
|
|
843 |
else
|
|
|
844 |
res = getStandardStreamsDestinationDefault();
|
|
|
845 |
return res;
|
25 |
ilm |
846 |
}
|
|
|
847 |
|
142 |
ilm |
848 |
// used if neither system property nor configuration property are set
|
|
|
849 |
protected StandardStreamsDest getStandardStreamsDestinationDefault() {
|
|
|
850 |
return Boolean.getBoolean(REDIRECT_TO_FILE) ? StandardStreamsDest.ALSO_TO_FILE : StandardStreamsDest.DEFAULT;
|
|
|
851 |
}
|
|
|
852 |
|
28 |
ilm |
853 |
protected DateFormat getLogDateFormat() {
|
|
|
854 |
return DATE_FORMAT;
|
|
|
855 |
}
|
|
|
856 |
|
83 |
ilm |
857 |
private final File getValidLogDir(final String dirName) {
|
17 |
ilm |
858 |
final File logDir;
|
|
|
859 |
try {
|
|
|
860 |
final File softLogDir = new File(this.getWD() + "/" + dirName + "/" + getHostname() + "-" + System.getProperty("user.name"));
|
41 |
ilm |
861 |
// don't throw an exception if this fails, we'll fall back to homeLogDir
|
17 |
ilm |
862 |
softLogDir.mkdirs();
|
41 |
ilm |
863 |
if (softLogDir.canWrite()) {
|
17 |
ilm |
864 |
logDir = softLogDir;
|
41 |
ilm |
865 |
} else {
|
17 |
ilm |
866 |
final File homeLogDir = new File(System.getProperty("user.home") + "/." + this.getAppName() + "/" + dirName);
|
|
|
867 |
FileUtils.mkdir_p(homeLogDir);
|
41 |
ilm |
868 |
if (homeLogDir.canWrite())
|
|
|
869 |
logDir = homeLogDir;
|
|
|
870 |
else
|
|
|
871 |
throw new IOException("Home log directory not writeable : " + homeLogDir);
|
17 |
ilm |
872 |
}
|
41 |
ilm |
873 |
assert logDir.exists() && logDir.canWrite();
|
19 |
ilm |
874 |
System.out.println("Log directory: " + logDir.getAbsolutePath());
|
41 |
ilm |
875 |
} catch (final IOException e) {
|
17 |
ilm |
876 |
throw new IllegalStateException("unable to create log dir", e);
|
|
|
877 |
}
|
83 |
ilm |
878 |
return logDir;
|
|
|
879 |
}
|
|
|
880 |
|
142 |
ilm |
881 |
static public enum StandardStreamsDest {
|
|
|
882 |
DEFAULT(true), ONLY_TO_FILE(false), ALSO_TO_FILE(true);
|
|
|
883 |
private final boolean hasDefaultStreams;
|
|
|
884 |
|
|
|
885 |
private StandardStreamsDest(boolean hasDefaultStreams) {
|
|
|
886 |
this.hasDefaultStreams = hasDefaultStreams;
|
|
|
887 |
}
|
|
|
888 |
|
|
|
889 |
public final boolean hasDefaultStreams() {
|
|
|
890 |
return this.hasDefaultStreams;
|
|
|
891 |
}
|
|
|
892 |
}
|
|
|
893 |
|
|
|
894 |
public final void setupLogging(final String dirName, final StandardStreamsDest stdRedirect) {
|
83 |
ilm |
895 |
final File logDir;
|
|
|
896 |
synchronized (this.restLock) {
|
|
|
897 |
if (this.logDir != null)
|
|
|
898 |
throw new IllegalStateException("Already set to " + this.logDir);
|
|
|
899 |
logDir = getValidLogDir(dirName);
|
|
|
900 |
this.logDir = logDir;
|
|
|
901 |
}
|
28 |
ilm |
902 |
final String logNameBase = this.getAppName() + "_" + getLogDateFormat().format(new Date());
|
17 |
ilm |
903 |
|
41 |
ilm |
904 |
// must be done before setUpConsoleHandler(), otherwise log output not redirected
|
142 |
ilm |
905 |
if (stdRedirect != StandardStreamsDest.DEFAULT) {
|
17 |
ilm |
906 |
final File logFile = new File(logDir, (logNameBase + ".txt"));
|
|
|
907 |
try {
|
41 |
ilm |
908 |
FileUtils.mkdir_p(logFile.getParentFile());
|
142 |
ilm |
909 |
System.out.println("Standard output and error file: " + logFile.getAbsolutePath());
|
25 |
ilm |
910 |
final OutputStream fileOut = new FileOutputStream(logFile, true);
|
|
|
911 |
final OutputStream out, err;
|
142 |
ilm |
912 |
if (this.isInIDE() || stdRedirect != StandardStreamsDest.ONLY_TO_FILE) {
|
61 |
ilm |
913 |
System.out.println("Redirecting standard output to file and console");
|
142 |
ilm |
914 |
out = new MultipleOutputStream(fileOut, System.out);
|
61 |
ilm |
915 |
System.out.println("Redirecting error output to file and console");
|
142 |
ilm |
916 |
err = new MultipleOutputStream(fileOut, System.err);
|
25 |
ilm |
917 |
} else {
|
142 |
ilm |
918 |
System.out.println("Redirecting standard output to file");
|
25 |
ilm |
919 |
out = fileOut;
|
142 |
ilm |
920 |
System.out.println("Redirecting error output to file");
|
25 |
ilm |
921 |
err = fileOut;
|
|
|
922 |
}
|
|
|
923 |
System.setErr(new PrintStream(new BufferedOutputStream(err, 128), true));
|
|
|
924 |
System.setOut(new PrintStream(new BufferedOutputStream(out, 128), true));
|
73 |
ilm |
925 |
// Takes about 350ms so run it async
|
|
|
926 |
new Thread(new Runnable() {
|
|
|
927 |
@Override
|
|
|
928 |
public void run() {
|
|
|
929 |
try {
|
|
|
930 |
FileUtils.ln(logFile, new File(logDir, "last.log"));
|
|
|
931 |
} catch (final IOException e) {
|
|
|
932 |
// the link is not important
|
|
|
933 |
e.printStackTrace();
|
|
|
934 |
}
|
|
|
935 |
}
|
|
|
936 |
}).start();
|
|
|
937 |
} catch (final Exception e) {
|
93 |
ilm |
938 |
throw new IllegalStateException("Redirection des sorties standards impossible", e);
|
17 |
ilm |
939 |
}
|
19 |
ilm |
940 |
} else {
|
41 |
ilm |
941 |
System.out.println("Standard streams not redirected to file");
|
17 |
ilm |
942 |
}
|
|
|
943 |
|
|
|
944 |
// removes default
|
|
|
945 |
LogUtils.rmRootHandlers();
|
28 |
ilm |
946 |
// add console handler
|
17 |
ilm |
947 |
LogUtils.setUpConsoleHandler();
|
28 |
ilm |
948 |
// add file handler (supports concurrent launches, doesn't depend on date)
|
17 |
ilm |
949 |
try {
|
|
|
950 |
final File logFile = new File(logDir, this.getAppName() + "-%u-age%g.log");
|
41 |
ilm |
951 |
FileUtils.mkdir_p(logFile.getParentFile());
|
19 |
ilm |
952 |
System.out.println("Logger logs: " + logFile.getAbsolutePath());
|
17 |
ilm |
953 |
// 2 files of at most 5M, each new launch append
|
|
|
954 |
// if multiple concurrent launches %u is used
|
|
|
955 |
final FileHandler fh = new FileHandler(logFile.getPath(), 5 * 1024 * 1024, 2, true);
|
|
|
956 |
fh.setFormatter(new SimpleFormatter());
|
|
|
957 |
Logger.getLogger("").addHandler(fh);
|
73 |
ilm |
958 |
} catch (final Exception e) {
|
93 |
ilm |
959 |
throw new IllegalStateException("Enregistrement du Logger désactivé", e);
|
17 |
ilm |
960 |
}
|
|
|
961 |
|
|
|
962 |
this.setLoggersLevel();
|
|
|
963 |
}
|
|
|
964 |
|
41 |
ilm |
965 |
public final File getLogDir() {
|
83 |
ilm |
966 |
synchronized (this.restLock) {
|
|
|
967 |
return this.logDir;
|
|
|
968 |
}
|
41 |
ilm |
969 |
}
|
|
|
970 |
|
17 |
ilm |
971 |
public void tearDownLogging() {
|
|
|
972 |
this.tearDownLogging(Boolean.getBoolean(REDIRECT_TO_FILE));
|
|
|
973 |
}
|
|
|
974 |
|
|
|
975 |
public void tearDownLogging(final boolean redirectToFile) {
|
|
|
976 |
LogUtils.rmRootHandlers();
|
|
|
977 |
if (redirectToFile) {
|
|
|
978 |
System.out.close();
|
|
|
979 |
System.err.close();
|
|
|
980 |
}
|
|
|
981 |
}
|
|
|
982 |
|
|
|
983 |
protected SQLElementDirectory createDirectory() {
|
|
|
984 |
return new SQLElementDirectory();
|
|
|
985 |
}
|
|
|
986 |
|
156 |
ilm |
987 |
// Use resource name to be able to use absolute (beginning with /) or relative path (to this
|
|
|
988 |
// class)
|
17 |
ilm |
989 |
protected List<String> getMappings() {
|
73 |
ilm |
990 |
return Arrays.asList("mapping", "mapping-" + this.getProperty("customer"));
|
17 |
ilm |
991 |
}
|
|
|
992 |
|
142 |
ilm |
993 |
protected SQLFieldTranslator createTranslator(final SQLElementDirectory dir) {
|
17 |
ilm |
994 |
final List<String> mappings = getMappings();
|
|
|
995 |
if (mappings.size() == 0)
|
|
|
996 |
throw new IllegalStateException("empty mappings");
|
|
|
997 |
|
156 |
ilm |
998 |
final SQLFieldTranslator trns = new SQLFieldTranslator(this.getRoot(), dir);
|
73 |
ilm |
999 |
// perhaps listen to UserProps (as in TM)
|
|
|
1000 |
return loadTranslations(trns, this.getRoot(), mappings);
|
|
|
1001 |
}
|
|
|
1002 |
|
|
|
1003 |
protected final SQLFieldTranslator loadTranslations(final SQLFieldTranslator trns, final DBRoot root, final List<String> mappings) {
|
177 |
ilm |
1004 |
final Locale locale = this.getLocale();
|
73 |
ilm |
1005 |
final Control cntrl = TranslationManager.getControl();
|
|
|
1006 |
boolean found = false;
|
|
|
1007 |
// better to have a translation in the correct language than a translation for the correct
|
|
|
1008 |
// customer in the wrong language
|
|
|
1009 |
final String fakeBaseName = "";
|
|
|
1010 |
for (Locale targetLocale = locale; targetLocale != null && !found; targetLocale = cntrl.getFallbackLocale(fakeBaseName, targetLocale)) {
|
|
|
1011 |
final List<Locale> langs = cntrl.getCandidateLocales(fakeBaseName, targetLocale);
|
|
|
1012 |
// SQLFieldTranslator overwrite, so we need to load from general to specific
|
|
|
1013 |
final ListIterator<Locale> listIterator = CollectionUtils.getListIterator(langs, true);
|
|
|
1014 |
while (listIterator.hasNext()) {
|
|
|
1015 |
final Locale lang = listIterator.next();
|
156 |
ilm |
1016 |
final SQLElementNamesFromXML elemNames = new SQLElementNamesFromXML(lang);
|
|
|
1017 |
found |= loadTranslations(trns, PropsConfiguration.class.getResourceAsStream(cntrl.toBundleName("mapping", lang) + ".xml"), root, elemNames);
|
73 |
ilm |
1018 |
for (final String m : mappings) {
|
156 |
ilm |
1019 |
final String bundleName = cntrl.toBundleName(m, lang);
|
|
|
1020 |
found |= loadTranslations(trns, this.getStream(bundleName + ".xml"), root, elemNames);
|
|
|
1021 |
final Class<? extends TranslatorFiller> loadedClass = ReflectUtils.getSubclass(toClassName(bundleName), TranslatorFiller.class);
|
|
|
1022 |
if (loadedClass != null) {
|
|
|
1023 |
try {
|
|
|
1024 |
ReflectUtils.createInstance(loadedClass, this).fill(trns);
|
|
|
1025 |
} catch (Exception e) {
|
|
|
1026 |
Log.get().log(Level.WARNING, "Couldn't use " + loadedClass, e);
|
|
|
1027 |
}
|
|
|
1028 |
}
|
73 |
ilm |
1029 |
}
|
|
|
1030 |
}
|
17 |
ilm |
1031 |
}
|
|
|
1032 |
return trns;
|
|
|
1033 |
}
|
|
|
1034 |
|
156 |
ilm |
1035 |
@FunctionalInterface
|
|
|
1036 |
static public interface TranslatorFiller {
|
|
|
1037 |
void fill(final SQLFieldTranslator t);
|
|
|
1038 |
}
|
|
|
1039 |
|
|
|
1040 |
static public abstract class AbstractTranslatorFiller implements TranslatorFiller {
|
|
|
1041 |
private final PropsConfiguration conf;
|
|
|
1042 |
|
|
|
1043 |
public AbstractTranslatorFiller(final PropsConfiguration conf) {
|
|
|
1044 |
this.conf = conf;
|
|
|
1045 |
}
|
|
|
1046 |
|
|
|
1047 |
protected final PropsConfiguration getConf() {
|
|
|
1048 |
return this.conf;
|
|
|
1049 |
}
|
|
|
1050 |
}
|
|
|
1051 |
|
|
|
1052 |
private final boolean loadTranslations(final SQLFieldTranslator trns, final InputStream in, final DBRoot root, final SQLElementNamesFromXML elemNames) {
|
73 |
ilm |
1053 |
final boolean res = in != null;
|
|
|
1054 |
// do not force to have one mapping for each client and each locale
|
|
|
1055 |
if (res)
|
156 |
ilm |
1056 |
trns.load(root, in, elemNames);
|
73 |
ilm |
1057 |
return res;
|
|
|
1058 |
}
|
|
|
1059 |
|
17 |
ilm |
1060 |
protected File createWD() {
|
|
|
1061 |
return new File(this.getProperty("wd"));
|
|
|
1062 |
}
|
|
|
1063 |
|
142 |
ilm |
1064 |
protected BaseDirs createBaseDirs() {
|
|
|
1065 |
return BaseDirs.create(getProductInfo(), getAppVariant());
|
|
|
1066 |
}
|
|
|
1067 |
|
17 |
ilm |
1068 |
// *** add
|
|
|
1069 |
|
|
|
1070 |
/**
|
|
|
1071 |
* Add the passed Configuration to this. If an item is not already created, this method won't,
|
|
|
1072 |
* instead the item to add will be stored. Also items of this won't be replaced by those of
|
|
|
1073 |
* <code>conf</code>.
|
|
|
1074 |
*
|
|
|
1075 |
* @param conf the conf to add.
|
|
|
1076 |
*/
|
41 |
ilm |
1077 |
@Override
|
|
|
1078 |
public final Configuration add(final Configuration conf) {
|
83 |
ilm |
1079 |
this.directory.add(conf);
|
|
|
1080 |
return this;
|
|
|
1081 |
}
|
17 |
ilm |
1082 |
|
83 |
ilm |
1083 |
private abstract class Addable<T> {
|
17 |
ilm |
1084 |
|
83 |
ilm |
1085 |
@GuardedBy("this")
|
|
|
1086 |
private boolean destroyed;
|
|
|
1087 |
@GuardedBy("this")
|
|
|
1088 |
private final List<Configuration> toAdd;
|
|
|
1089 |
@GuardedBy("this")
|
|
|
1090 |
private Future<T> f;
|
17 |
ilm |
1091 |
|
83 |
ilm |
1092 |
protected Addable() {
|
|
|
1093 |
super();
|
|
|
1094 |
synchronized (this) {
|
|
|
1095 |
this.toAdd = new ArrayList<Configuration>();
|
|
|
1096 |
this.f = null;
|
|
|
1097 |
this.destroyed = false;
|
|
|
1098 |
}
|
|
|
1099 |
}
|
|
|
1100 |
|
|
|
1101 |
public final void add(final Configuration conf) {
|
|
|
1102 |
final boolean computeStarted;
|
|
|
1103 |
synchronized (this) {
|
|
|
1104 |
computeStarted = isComputeStarted();
|
|
|
1105 |
if (!computeStarted)
|
|
|
1106 |
this.toAdd.add(conf);
|
|
|
1107 |
}
|
|
|
1108 |
if (computeStarted) {
|
|
|
1109 |
// T must be thread-safe
|
|
|
1110 |
add(this.get(), conf);
|
|
|
1111 |
}
|
|
|
1112 |
}
|
|
|
1113 |
|
|
|
1114 |
// synchronize on this (and not some private lock) to allow callers to do something before
|
|
|
1115 |
// the result changes
|
|
|
1116 |
protected final boolean isComputeStarted() {
|
|
|
1117 |
synchronized (this) {
|
|
|
1118 |
return this.f != null;
|
|
|
1119 |
}
|
|
|
1120 |
}
|
|
|
1121 |
|
|
|
1122 |
public final T get() {
|
|
|
1123 |
// result
|
|
|
1124 |
final Future<T> future;
|
|
|
1125 |
// to run
|
|
|
1126 |
final FutureTask<T> futureTask;
|
|
|
1127 |
synchronized (this) {
|
|
|
1128 |
checkDestroyed(this.destroyed);
|
|
|
1129 |
if (this.f == null) {
|
|
|
1130 |
final List<Configuration> l = new ArrayList<Configuration>(this.toAdd);
|
|
|
1131 |
this.toAdd.clear();
|
|
|
1132 |
futureTask = new FutureTask<T>(new Callable<T>() {
|
|
|
1133 |
@Override
|
|
|
1134 |
public T call() throws Exception {
|
|
|
1135 |
final T res = create();
|
|
|
1136 |
// don't call alien code with lock
|
|
|
1137 |
assert !Thread.holdsLock(Addable.this);
|
|
|
1138 |
for (final Configuration s : l) {
|
|
|
1139 |
// deadlock if get() is called (will hang on future.get())
|
|
|
1140 |
add(res, s);
|
|
|
1141 |
}
|
|
|
1142 |
return res;
|
|
|
1143 |
}
|
|
|
1144 |
});
|
|
|
1145 |
this.f = futureTask;
|
|
|
1146 |
future = futureTask;
|
|
|
1147 |
} else {
|
|
|
1148 |
futureTask = null;
|
|
|
1149 |
future = this.f;
|
|
|
1150 |
}
|
|
|
1151 |
}
|
|
|
1152 |
if (futureTask != null)
|
|
|
1153 |
futureTask.run();
|
|
|
1154 |
try {
|
|
|
1155 |
return future.get();
|
|
|
1156 |
} catch (InterruptedException e) {
|
|
|
1157 |
throw new RTInterruptedException(e);
|
|
|
1158 |
} catch (ExecutionException e) {
|
|
|
1159 |
throw new IllegalStateException(e);
|
|
|
1160 |
}
|
|
|
1161 |
}
|
|
|
1162 |
|
|
|
1163 |
protected abstract T create();
|
|
|
1164 |
|
|
|
1165 |
protected abstract void add(final T obj, final Configuration conf);
|
|
|
1166 |
|
|
|
1167 |
public final void destroy() {
|
|
|
1168 |
final Future<T> future;
|
|
|
1169 |
synchronized (this) {
|
|
|
1170 |
this.destroyed = true;
|
|
|
1171 |
future = this.f;
|
|
|
1172 |
}
|
|
|
1173 |
if (future != null)
|
|
|
1174 |
destroy(future);
|
|
|
1175 |
}
|
|
|
1176 |
|
|
|
1177 |
// nothing by default
|
|
|
1178 |
protected void destroy(final Future<T> future) {
|
|
|
1179 |
}
|
17 |
ilm |
1180 |
}
|
|
|
1181 |
|
|
|
1182 |
// *** getters
|
|
|
1183 |
|
41 |
ilm |
1184 |
@Override
|
17 |
ilm |
1185 |
public final ShowAs getShowAs() {
|
132 |
ilm |
1186 |
return this.getDirectory().getShowAs();
|
17 |
ilm |
1187 |
}
|
|
|
1188 |
|
41 |
ilm |
1189 |
@Override
|
17 |
ilm |
1190 |
public final SQLBase getBase() {
|
|
|
1191 |
return this.getNode(SQLBase.class);
|
|
|
1192 |
}
|
|
|
1193 |
|
41 |
ilm |
1194 |
@Override
|
17 |
ilm |
1195 |
public final DBRoot getRoot() {
|
|
|
1196 |
synchronized (this.treeLock) {
|
83 |
ilm |
1197 |
checkDestroyed();
|
17 |
ilm |
1198 |
if (this.root == null)
|
|
|
1199 |
this.setRoot(this.createRoot());
|
83 |
ilm |
1200 |
return this.root;
|
17 |
ilm |
1201 |
}
|
|
|
1202 |
}
|
|
|
1203 |
|
144 |
ilm |
1204 |
public final UserManager getUserManager() {
|
|
|
1205 |
synchronized (this.treeLock) {
|
|
|
1206 |
getRoot();
|
|
|
1207 |
return this.uMngr;
|
|
|
1208 |
}
|
|
|
1209 |
}
|
|
|
1210 |
|
|
|
1211 |
public final UserRightsManager getUserRightsManager() {
|
|
|
1212 |
synchronized (this.treeLock) {
|
|
|
1213 |
getRoot();
|
|
|
1214 |
return this.urMngr;
|
|
|
1215 |
}
|
|
|
1216 |
}
|
|
|
1217 |
|
17 |
ilm |
1218 |
@Override
|
|
|
1219 |
public final DBSystemRoot getSystemRoot() {
|
|
|
1220 |
synchronized (this.treeLock) {
|
83 |
ilm |
1221 |
checkDestroyed();
|
17 |
ilm |
1222 |
if (this.sysRoot == null)
|
|
|
1223 |
this.sysRoot = this.createSystemRoot();
|
83 |
ilm |
1224 |
return this.sysRoot;
|
17 |
ilm |
1225 |
}
|
|
|
1226 |
}
|
|
|
1227 |
|
149 |
ilm |
1228 |
public final Thread createDBCheckThread(final JFrame mainFrame, final Runnable quitRunnable) {
|
|
|
1229 |
final DBSystemRoot sysRoot = this.getSystemRoot();
|
|
|
1230 |
final String quit = "Quitter le logiciel";
|
|
|
1231 |
final JOptionPane optionPane = new JOptionPane("Impossible de contacter la base. Cette fenêtre se fermera dès le rétablissement de la connexion. Sinon vous pouvez quitter le logiciel.",
|
|
|
1232 |
JOptionPane.DEFAULT_OPTION, JOptionPane.ERROR_MESSAGE, null, new Object[] { quit }, null);
|
|
|
1233 |
final JDialog dialog = optionPane.createDialog(mainFrame, "Erreur de connexion");
|
|
|
1234 |
dialog.setModalityType(ModalityType.APPLICATION_MODAL);
|
|
|
1235 |
// can only be closed by us (if connection is restored) or by choosing quit
|
|
|
1236 |
dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
|
|
|
1237 |
dialog.addComponentListener(new ComponentAdapter() {
|
|
|
1238 |
@Override
|
|
|
1239 |
public void componentHidden(ComponentEvent e) {
|
|
|
1240 |
if (optionPane.getValue().equals(quit))
|
|
|
1241 |
quitRunnable.run();
|
|
|
1242 |
else
|
|
|
1243 |
// only the thread can change the dialog, otherwise pb would be incoherent
|
|
|
1244 |
dialog.setVisible(true);
|
|
|
1245 |
}
|
|
|
1246 |
});
|
|
|
1247 |
dialog.pack();
|
|
|
1248 |
final Thread dbConn = new Thread(new Runnable() {
|
|
|
1249 |
@Override
|
|
|
1250 |
public void run() {
|
|
|
1251 |
boolean pb = false;
|
|
|
1252 |
while (true) {
|
|
|
1253 |
try {
|
|
|
1254 |
Thread.sleep((pb ? 4 : 20) * 1000);
|
|
|
1255 |
} catch (InterruptedException e1) {
|
|
|
1256 |
// ignore
|
|
|
1257 |
e1.printStackTrace();
|
|
|
1258 |
}
|
|
|
1259 |
try {
|
|
|
1260 |
sysRoot.getDataSource().validateDBConnectivity();
|
|
|
1261 |
if (pb) {
|
|
|
1262 |
SwingUtilities.invokeLater(new Runnable() {
|
|
|
1263 |
@Override
|
|
|
1264 |
public void run() {
|
|
|
1265 |
// don't use setVisible(false) otherwise
|
|
|
1266 |
// componentHidden() will be called
|
|
|
1267 |
dialog.dispose();
|
|
|
1268 |
}
|
|
|
1269 |
});
|
|
|
1270 |
pb = false;
|
|
|
1271 |
}
|
|
|
1272 |
} catch (Exception e) {
|
|
|
1273 |
if (!pb) {
|
|
|
1274 |
pb = true;
|
|
|
1275 |
e.printStackTrace();
|
|
|
1276 |
SwingUtilities.invokeLater(new Runnable() {
|
|
|
1277 |
@Override
|
|
|
1278 |
public void run() {
|
|
|
1279 |
dialog.setLocationRelativeTo(dialog.getOwner());
|
|
|
1280 |
dialog.setVisible(true);
|
|
|
1281 |
}
|
|
|
1282 |
});
|
|
|
1283 |
}
|
|
|
1284 |
}
|
|
|
1285 |
}
|
|
|
1286 |
}
|
|
|
1287 |
}, "databaseConnectivity");
|
|
|
1288 |
dbConn.setDaemon(true);
|
|
|
1289 |
return dbConn;
|
|
|
1290 |
}
|
|
|
1291 |
|
17 |
ilm |
1292 |
/**
|
|
|
1293 |
* Get the node of the asked class, creating just the necessary instances (ie getNode(Server)
|
|
|
1294 |
* won't do a getBase().getServer()).
|
|
|
1295 |
*
|
|
|
1296 |
* @param <T> the type wanted.
|
|
|
1297 |
* @param clazz the class wanted, eg SQLBase.class, DBSystemRoot.class.
|
|
|
1298 |
* @return the corresponding instance, eg getBase() for SQLBase, getServer() or getBase() for
|
|
|
1299 |
* DBSystemRoot depending on the SQL system.
|
|
|
1300 |
*/
|
41 |
ilm |
1301 |
public final <T extends DBStructureItem<?>> T getNode(final Class<T> clazz) {
|
17 |
ilm |
1302 |
final SQLSystem sys = this.getServer().getSQLSystem();
|
|
|
1303 |
final HierarchyLevel l = sys.getLevel(clazz);
|
|
|
1304 |
if (l == HierarchyLevel.SQLSERVER)
|
|
|
1305 |
return this.getServer().getAnc(clazz);
|
|
|
1306 |
else if (l == sys.getLevel(DBSystemRoot.class))
|
|
|
1307 |
return this.getSystemRoot().getAnc(clazz);
|
|
|
1308 |
else if (l == sys.getLevel(DBRoot.class))
|
|
|
1309 |
return this.getRoot().getAnc(clazz);
|
|
|
1310 |
else
|
|
|
1311 |
throw new IllegalArgumentException("doesn't know an item of " + clazz);
|
|
|
1312 |
}
|
|
|
1313 |
|
|
|
1314 |
public final SQLServer getServer() {
|
|
|
1315 |
return this.getServer(true);
|
|
|
1316 |
}
|
|
|
1317 |
|
|
|
1318 |
private final SQLServer getServer(final boolean initSysRoot) {
|
|
|
1319 |
synchronized (this.treeLock) {
|
83 |
ilm |
1320 |
checkDestroyed();
|
17 |
ilm |
1321 |
if (this.server == null) {
|
|
|
1322 |
this.setServer(this.createServer());
|
|
|
1323 |
// necessary otherwise the returned server has no datasource
|
|
|
1324 |
// (eg getChildren() will fail)
|
|
|
1325 |
if (initSysRoot && this.server.getSQLSystem().getLevel(DBSystemRoot.class) == HierarchyLevel.SQLSERVER)
|
|
|
1326 |
this.getSystemRoot();
|
|
|
1327 |
}
|
83 |
ilm |
1328 |
return this.server;
|
17 |
ilm |
1329 |
}
|
|
|
1330 |
}
|
|
|
1331 |
|
41 |
ilm |
1332 |
@Override
|
17 |
ilm |
1333 |
public final SQLFilter getFilter() {
|
|
|
1334 |
synchronized (this.restLock) {
|
|
|
1335 |
if (this.filter == null)
|
|
|
1336 |
this.setFilter(this.createFilter());
|
83 |
ilm |
1337 |
return this.filter;
|
17 |
ilm |
1338 |
}
|
|
|
1339 |
}
|
|
|
1340 |
|
41 |
ilm |
1341 |
@Override
|
17 |
ilm |
1342 |
public final SQLElementDirectory getDirectory() {
|
83 |
ilm |
1343 |
return this.directory.get();
|
17 |
ilm |
1344 |
}
|
|
|
1345 |
|
41 |
ilm |
1346 |
public final ProductInfo getProductInfo() {
|
83 |
ilm |
1347 |
synchronized (this.restLock) {
|
|
|
1348 |
return this.productInfo;
|
|
|
1349 |
}
|
41 |
ilm |
1350 |
}
|
|
|
1351 |
|
17 |
ilm |
1352 |
@Override
|
41 |
ilm |
1353 |
public final String getAppName() {
|
73 |
ilm |
1354 |
final ProductInfo productInfo = this.getProductInfo();
|
|
|
1355 |
if (productInfo != null)
|
|
|
1356 |
return productInfo.getName();
|
|
|
1357 |
else
|
|
|
1358 |
return this.getProperty("app.name");
|
17 |
ilm |
1359 |
}
|
|
|
1360 |
|
|
|
1361 |
@Override
|
|
|
1362 |
public final File getWD() {
|
|
|
1363 |
synchronized (this.restLock) {
|
|
|
1364 |
if (this.wd == null)
|
|
|
1365 |
this.setWD(this.createWD());
|
83 |
ilm |
1366 |
return this.wd;
|
17 |
ilm |
1367 |
}
|
|
|
1368 |
}
|
|
|
1369 |
|
142 |
ilm |
1370 |
@Override
|
|
|
1371 |
public BaseDirs getBaseDirs() {
|
|
|
1372 |
synchronized (this.restLock) {
|
|
|
1373 |
if (this.baseDirs == null)
|
|
|
1374 |
this.baseDirs = this.createBaseDirs();
|
|
|
1375 |
return this.baseDirs;
|
|
|
1376 |
}
|
|
|
1377 |
}
|
|
|
1378 |
|
177 |
ilm |
1379 |
@Override
|
|
|
1380 |
public Locale getLocale() {
|
|
|
1381 |
synchronized (this.restLock) {
|
|
|
1382 |
return this.locale;
|
|
|
1383 |
}
|
|
|
1384 |
}
|
|
|
1385 |
|
17 |
ilm |
1386 |
// *** setters
|
|
|
1387 |
|
|
|
1388 |
// MAYBE add synchronized (not necessary since they're private, and only called with the lock)
|
|
|
1389 |
|
41 |
ilm |
1390 |
private final void setFilter(final SQLFilter filter) {
|
17 |
ilm |
1391 |
this.filter = filter;
|
|
|
1392 |
}
|
|
|
1393 |
|
41 |
ilm |
1394 |
private void setServer(final SQLServer server) {
|
17 |
ilm |
1395 |
this.server = server;
|
|
|
1396 |
}
|
|
|
1397 |
|
41 |
ilm |
1398 |
private final void setRoot(final DBRoot root) {
|
17 |
ilm |
1399 |
this.root = root;
|
83 |
ilm |
1400 |
checkDestroyed();
|
73 |
ilm |
1401 |
// be sure to try to set a manager to avoid giving all permissions to everyone
|
|
|
1402 |
this.urMngr = createUserRightsManager(root);
|
144 |
ilm |
1403 |
this.uMngr = UserManager.getSingletonManager().setInstanceIfNone(root);
|
17 |
ilm |
1404 |
}
|
|
|
1405 |
|
41 |
ilm |
1406 |
private final void setWD(final File dir) {
|
17 |
ilm |
1407 |
this.wd = dir;
|
|
|
1408 |
}
|
73 |
ilm |
1409 |
|
177 |
ilm |
1410 |
public void setLocale(Locale locale) {
|
|
|
1411 |
Objects.requireNonNull(locale);
|
|
|
1412 |
// don't create the directory
|
|
|
1413 |
final boolean localeChangedAndDirExists;
|
|
|
1414 |
synchronized (this.restLock) {
|
|
|
1415 |
if (locale.equals(this.locale)) {
|
|
|
1416 |
localeChangedAndDirExists = false;
|
|
|
1417 |
} else {
|
|
|
1418 |
this.locale = locale;
|
|
|
1419 |
localeChangedAndDirExists = this.directory.isComputeStarted();
|
|
|
1420 |
}
|
|
|
1421 |
}
|
|
|
1422 |
if (localeChangedAndDirExists) {
|
|
|
1423 |
final SQLElementDirectory dir = this.getDirectory();
|
|
|
1424 |
dir.setTranslator(createTranslator(dir));
|
|
|
1425 |
}
|
|
|
1426 |
}
|
|
|
1427 |
|
73 |
ilm |
1428 |
public FieldMapper getFieldMapper() {
|
|
|
1429 |
return fieldMapper;
|
|
|
1430 |
}
|
|
|
1431 |
|
93 |
ilm |
1432 |
public void setFieldMapper(FieldMapper fieldMapper) {
|
73 |
ilm |
1433 |
this.fieldMapper = fieldMapper;
|
|
|
1434 |
}
|
17 |
ilm |
1435 |
}
|