Project

General

Profile

1 8452 slaughter
package edu.ucsb.nceas.metacat.dataone;
2
3
/**
4
 *  '$RCSfile$'
5
 *    Purpose: A Class for upgrading the database to version 1.5
6
 *  Copyright: 2000 Regents of the University of California and the
7
 *             National Center for Ecological Analysis and Synthesis
8
 *    Authors: Peter Slaughter
9
 *
10 8510 slaughter
 *   '$Author$'
11 8511 slaughter
 *     '$Date$'
12
 * '$Revision$'
13 8452 slaughter
 *
14
 * This program is free software; you can redistribute it and/or modify
15
 * it under the terms of the GNU General Public License as published by
16
 * the Free Software Foundation; either version 2 of the License, or
17
 * (at your option) any later version.
18
 *
19
 * This program is distributed in the hope that it will be useful,
20
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22
 * GNU General Public License for more details.
23
 *
24
 * You should have received a copy of the GNU General Public License
25
 * along with this program; if not, write to the Free Software
26
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27
 */
28
29
import java.math.BigInteger;
30
import java.sql.SQLException;
31
import java.util.ArrayList;
32
import java.util.Arrays;
33
import java.util.Date;
34
import java.util.HashMap;
35
import java.util.HashSet;
36
import java.util.List;
37
import java.util.Map;
38
import java.util.Set;
39 8590 slaughter
import java.util.concurrent.ExecutorService;
40
import java.util.concurrent.Executors;
41 8452 slaughter
42
import org.apache.log4j.Logger;
43 8810 leinfelder
import org.dataone.client.v2.CNode;
44
import org.dataone.client.v2.itk.D1Client;
45 8452 slaughter
import org.dataone.service.exceptions.InvalidRequest;
46
import org.dataone.service.exceptions.InvalidToken;
47
import org.dataone.service.exceptions.NotAuthorized;
48
import org.dataone.service.exceptions.NotFound;
49
import org.dataone.service.exceptions.NotImplemented;
50
import org.dataone.service.exceptions.ServiceFailure;
51
import org.dataone.service.exceptions.VersionMismatch;
52
import org.dataone.service.types.v1.AccessPolicy;
53 8614 leinfelder
import org.dataone.service.types.v1.AccessRule;
54 8452 slaughter
import org.dataone.service.types.v1.Identifier;
55 9249 tao
import org.dataone.service.types.v1.NodeReference;
56 8452 slaughter
import org.dataone.service.types.v1.ObjectFormatIdentifier;
57
import org.dataone.service.types.v1.ObjectInfo;
58
import org.dataone.service.types.v1.ObjectList;
59
import org.dataone.service.types.v1.Permission;
60
import org.dataone.service.types.v1.Session;
61
import org.dataone.service.types.v1.Subject;
62 8810 leinfelder
import org.dataone.service.types.v2.SystemMetadata;
63 8452 slaughter
64
import edu.ucsb.nceas.metacat.AccessionNumberException;
65
import edu.ucsb.nceas.metacat.IdentifierManager;
66
import edu.ucsb.nceas.metacat.McdbDocNotFoundException;
67 8561 slaughter
import edu.ucsb.nceas.metacat.accesscontrol.AccessControlException;
68 8452 slaughter
import edu.ucsb.nceas.metacat.properties.PropertyService;
69
import edu.ucsb.nceas.metacat.shared.ServiceException;
70 8610 slaughter
import edu.ucsb.nceas.utilities.GeneralPropertyException;
71 8452 slaughter
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
72
import edu.ucsb.nceas.utilities.SortedProperties;
73
74
public class SyncAccessPolicy {
75
76
	private static Logger logMetacat = Logger.getLogger(SyncAccessPolicy.class);
77 8590 slaughter
78 8452 slaughter
	/**
79
	 * Synchronize access policy (from system metadata) of d1 member node with
80
	 * the corresponding controlling node.
81
	 *
82
	 * @param objList
83
	 *            list of d1 objects to be synced
84
	 * @return syncedIds a list of pids that were synced with the CN
85
	 * @throws ServiceFailure
86
	 * @throws InvalidToken
87
	 * @throws NotAuthorized
88
	 * @throws NotFound
89
	 * @throws NotImplemented
90
	 * @throws McdbDocNotFoundException
91
	 * @throws InvalidRequest
92
	 * @throws VersionMismatch
93
	 * @throws SQLException
94
	 * @throws AccessionNumberException
95
	 * @throws NumberFormatException
96
	 */
97 8585 leinfelder
	private List<Identifier> sync(ObjectList objList) throws ServiceFailure,
98 8452 slaughter
			InvalidToken, NotAuthorized, NotFound, NotImplemented,
99
			McdbDocNotFoundException, InvalidRequest, VersionMismatch,
100 8590 slaughter
			NumberFormatException, AccessionNumberException, SQLException,
101
			Exception {
102 8452 slaughter
103
		AccessPolicy cnAccessPolicy = null;
104
		AccessPolicy mnAccessPolicy = null;
105 8510 slaughter
		Identifier pid = new Identifier();
106 8452 slaughter
		ObjectInfo objInfo = null;
107
		Session session = null;
108
		List<Identifier> syncedIds = new ArrayList<Identifier>();
109
		SystemMetadata cnSysMeta = null;
110
		SystemMetadata mnSysMeta = null;
111
112 8561 slaughter
		CNode cn = null;
113 8590 slaughter
114 8561 slaughter
		try {
115
			cn = D1Client.getCN();
116 8592 slaughter
			logMetacat.debug("Will sync access policies to CN id: "
117
					+ cn.getNodeId() + " with info: " + cn.toString());
118 8561 slaughter
		} catch (ServiceFailure sf) {
119 8590 slaughter
			logMetacat
120
					.error("Unable to get Coordinating node name for this MN");
121
			throw new AccessControlException(
122
					"Unable to get Coordinating node name for this MN");
123 8561 slaughter
		}
124 8452 slaughter
125 8491 slaughter
		for (int i = objList.getStart(); i < objList.getCount(); i++) {
126 8452 slaughter
127
			objInfo = objList.getObjectInfo(i);
128
			pid = objInfo.getIdentifier();
129 8491 slaughter
130 8590 slaughter
			logMetacat.debug("Getting SM for pid: " + pid.getValue() + " i: "
131
					+ i);
132 8452 slaughter
			try {
133
				// Get sm, access policy for requested localId
134
				mnSysMeta = IdentifierManager.getInstance().getSystemMetadata(
135
						pid.getValue());
136
			} catch (McdbDocNotFoundException e) {
137
				logMetacat.error("Error syncing access policy of pid: "
138
						+ pid.getValue() + " pid not found: " + e.getMessage());
139 8585 leinfelder
				continue;
140 8452 slaughter
			} catch (Exception e) {
141
				logMetacat.error("Error syncing access policy of pid: "
142 8585 leinfelder
						+ pid.getValue() + ". Message: " + e.getMessage());
143
				continue;
144 8452 slaughter
			}
145
146 8590 slaughter
			logMetacat
147
					.debug("Getting access policy for pid: " + pid.getValue());
148 8491 slaughter
149 8452 slaughter
			mnAccessPolicy = mnSysMeta.getAccessPolicy();
150 8590 slaughter
151 8452 slaughter
			// Get sm, access policy for requested pid from the CN
152
			try {
153 8810 leinfelder
				cnSysMeta = cn.getSystemMetadata(null, pid);
154 8452 slaughter
			} catch (Exception e) {
155
				logMetacat.error("Error getting system metadata for pid: "
156
						+ pid.getValue() + " from cn: " + e.getMessage());
157 8510 slaughter
				continue;
158 8452 slaughter
			}
159 8491 slaughter
			logMetacat.debug("Getting access policy from CN for pid: "
160
					+ pid.getValue());
161 8452 slaughter
			cnAccessPolicy = cnSysMeta.getAccessPolicy();
162 8491 slaughter
			logMetacat.debug("Diffing access policies (MN,CN) for pid: "
163
					+ pid.getValue());
164 8452 slaughter
165 8491 slaughter
			// Compare access policies of MN and CN, and update if different.
166 8452 slaughter
			if (!isEqual(mnAccessPolicy, cnAccessPolicy)) {
167
				try {
168
					BigInteger serialVersion = cnSysMeta.getSerialVersion();
169 8590 slaughter
					logMetacat
170
							.debug("Requesting CN to set access policy for pid: "
171
									+ pid.getValue()
172
									+ ", serial version: "
173
									+ serialVersion.toString());
174 8491 slaughter
					cn.setAccessPolicy(session, pid, mnAccessPolicy,
175 8452 slaughter
							serialVersion.longValue());
176 8610 slaughter
					logMetacat.debug("Successfully set access policy for pid: " + pid.getValue());
177 8491 slaughter
					// Add this pid to the list of pids that were successfully
178
					// synced
179 8452 slaughter
					syncedIds.add(pid);
180 8510 slaughter
				} catch (NotAuthorized na) {
181
					logMetacat
182
							.error("Error syncing CN with access policy of pid: "
183
									+ pid.getValue()
184
									+ " user not authorized: "
185
									+ na.getMessage());
186 8590 slaughter
					// throw na;
187 8585 leinfelder
					continue;
188 8510 slaughter
				} catch (ServiceFailure sf) {
189
					logMetacat
190
							.error("Error syncing CN with access policy of pid: "
191
									+ pid.getValue()
192
									+ " Service failure: "
193 8595 slaughter
									+ "'" + sf.getDescription() + "'");
194 8592 slaughter
					sf.printStackTrace();
195 8595 slaughter
					logMetacat.debug("Cause: " + "'" + sf.getCause() + "'");
196 8590 slaughter
					// throw sf;
197 8585 leinfelder
					continue;
198 8452 slaughter
				} catch (Exception e) {
199 8510 slaughter
					logMetacat
200
							.error("Error syncing CN with access policy of pid: "
201
									+ pid.getValue() + e.getMessage());
202 8590 slaughter
					// throw e;
203 8585 leinfelder
					continue;
204 8452 slaughter
				}
205 8585 leinfelder
			} else {
206
				logMetacat.warn("Skipping pid: " + pid.getValue());
207 8452 slaughter
			}
208 8610 slaughter
			logMetacat.debug("Done checking access policy for pid: "
209 8590 slaughter
					+ pid.getValue());
210 8452 slaughter
		}
211
212
		return syncedIds;
213
	}
214
215
	/**
216
	 * Convenience function that accepts a list of guids to sync
217
	 *
218
	 * @param guidsToSync
219
	 *            list of guids to have access policy synced for
220
	 * @return syncedPids - list of pids that were actually synced with the CN
221
	 * @throws NumberFormatException
222
	 * @throws ServiceFailure
223
	 * @throws InvalidToken
224
	 * @throws NotAuthorized
225
	 * @throws NotFound
226
	 * @throws NotImplemented
227
	 * @throws McdbDocNotFoundException
228
	 * @throws InvalidRequest
229
	 * @throws VersionMismatch
230
	 * @throws AccessionNumberException
231
	 * @throws SQLException
232
	 */
233
	public List<Identifier> sync(List<String> guidsToSync)
234
			throws NumberFormatException, ServiceFailure, InvalidToken,
235
			NotAuthorized, NotFound, NotImplemented, McdbDocNotFoundException,
236
			InvalidRequest, VersionMismatch, AccessionNumberException,
237 8511 slaughter
			SQLException, Exception {
238 8452 slaughter
		List<Identifier> syncedPids = null;
239
		ObjectList objList = new ObjectList();
240
		SystemMetadata sm = new SystemMetadata();
241
242
		int start = 0;
243 8590 slaughter
		int count = 0; // guidsToSync.size();
244 8452 slaughter
245
		objList.setStart(start);
246
247
		// Convert the guids to d1 objects, as this is what
248
		// IdentifierManager.getInstance().querySystemMetadata returns in
249
		// syncAll, and
250
		// what sync(ObjectList...) expects
251
		for (String guid : guidsToSync) {
252
			try {
253
				sm = IdentifierManager.getInstance().getSystemMetadata(guid);
254 8585 leinfelder
				count++;
255 8452 slaughter
			} catch (Exception e) {
256
				logMetacat.error("Error syncing access policy of pid: " + guid
257 8585 leinfelder
						+ ". Message: " + e.getMessage());
258
				continue;
259 8452 slaughter
			}
260
261
			ObjectInfo oi = new ObjectInfo();
262
			Identifier id = new Identifier();
263
			id.setValue(guid);
264
			oi.setIdentifier(id);
265
			oi.setDateSysMetadataModified(sm.getDateSysMetadataModified());
266
			oi.setChecksum(sm.getChecksum());
267
			oi.setFormatId(sm.getFormatId());
268
			oi.setSize(sm.getSize());
269
			objList.addObjectInfo(oi);
270
		}
271 8590 slaughter
272 8585 leinfelder
		int total = count;
273
		objList.setCount(count);
274
		objList.setTotal(total);
275 8452 slaughter
276
		syncedPids = sync(objList);
277
		return syncedPids;
278
	}
279
280 8590 slaughter
	/**
281
	 * For all guids for which current MN is authoritative, check that access
282
	 * policy is synced with CN.
283
	 *
284
	 * @return void
285
	 */
286
	public void syncAll() throws ServiceFailure, InvalidToken, NotAuthorized,
287
			NotFound, NotImplemented, McdbDocNotFoundException, InvalidRequest,
288
			VersionMismatch, NumberFormatException, AccessionNumberException,
289
			SQLException, PropertyNotFoundException, ServiceException,
290
			Exception {
291 8452 slaughter
292 8590 slaughter
		SyncTask st = new SyncTask();
293
		// Create a single thread to run the sync of all guids in
294
		ExecutorService executor = Executors.newSingleThreadExecutor();
295
		logMetacat.debug("syncAll starting thread");
296
		executor.execute(st);
297
		// Only one task will run on this thread
298
		executor.shutdown();
299 8452 slaughter
300 8590 slaughter
		// return syncedIds;
301
	}
302 8452 slaughter
303 8590 slaughter
	/**
304
	 * Perform syncAll in a single thread.
305
	 *
306
	 * @return void
307
	 */
308
	private class SyncTask implements Runnable {
309 8452 slaughter
310 8590 slaughter
		@Override
311
		public void run() {
312 8610 slaughter
313 8590 slaughter
			// For the following query parameters - null indicates that the
314
			// query
315
			// will not be
316
			// constrained by the parameter.
317
			Date startTime = null;
318
			Date endTime = null;
319
			ObjectFormatIdentifier objectFormatId = null;
320 9249 tao
			//Boolean replicaStatus = false; // return only pids for which this mn
321
			NodeReference thisMN = new NodeReference();
322
			try {
323
			    String currentNodeId = PropertyService.getInstance().getProperty("dataone.nodeId"); // return only pids for which this mn
324
			    thisMN.setValue(currentNodeId);
325
			} catch (Exception e) {
326
			    logMetacat.error("SyncAccessPolicy.run - can't get the node id of this member node from the metacat property file since :"+e.getMessage());
327
			    return;
328
			}
329
							// is
330 8590 slaughter
			ObjectList objsToSync = null;
331
			Integer count = 0;
332
			Integer start = 0;
333
			Integer total = 0;
334
			List<Identifier> tmpIds = null;
335 8610 slaughter
			// If even one sync error encounted, don't set property that will disable
336
			// "syncAll" button in admin/replication web page.
337
			boolean syncError = false;
338 8590 slaughter
			List<Identifier> syncedIds = new ArrayList<Identifier>();
339 8610 slaughter
340 8590 slaughter
			try {
341
				count = Integer.valueOf(PropertyService
342
						.getProperty("database.webResultsetSize"));
343
			} catch (NumberFormatException e1) {
344
				logMetacat
345
						.error("Error in  propery file for format of database.webResultsetSize, will use 1000");
346
				e1.printStackTrace();
347
				count = 1000;
348
			} catch (PropertyNotFoundException e1) {
349
				logMetacat
350
						.error("Error reading propery file for database.webResultsetSize, will use 1000");
351
				e1.printStackTrace();
352
				count = 1000;
353
			}
354
355
			// Get the total count of guids before we start syncing
356 9072 tao
			Identifier id = null;
357
            boolean isSid = false;
358 8590 slaughter
			try {
359 9072 tao
360 8590 slaughter
				objsToSync = IdentifierManager.getInstance()
361
						.querySystemMetadata(startTime, endTime,
362 9249 tao
								objectFormatId, thisMN, start, count, id, isSid);
363 8590 slaughter
364
				logMetacat.debug("syncTask total # of guids: "
365 8592 slaughter
						+ objsToSync.getTotal() + ", count for this page: "
366
						+ objsToSync.getCount());
367 8590 slaughter
			} catch (Exception e) {
368
				logMetacat.error("Error syncing ids");
369
			}
370 8592 slaughter
371 8590 slaughter
			total = objsToSync.getTotal();
372
373 8592 slaughter
			// The first loop might have fewer results than the requested count
374
			// value from the properties file,
375
			// so in this case use count returned from IdentiferManger for the
376
			// loop count/increment (loop will only execute once).
377
			if (objsToSync.getCount() < count)
378
				count = objsToSync.getCount();
379
380
			for (int i = 0; (i + count - 1) < total; i += count) {
381 8590 slaughter
				try {
382
					logMetacat.debug("syncTask # requested: " + count
383
							+ ", start: " + start + ", total: " + total
384
							+ ", count: " + objsToSync.getCount());
385
					tmpIds = sync(objsToSync);
386
					syncedIds.addAll(tmpIds);
387 8592 slaughter
388 8590 slaughter
					// Set start for the next db retrieval, loop interation
389
					start += objsToSync.getCount();
390 8592 slaughter
					if (start >= total)
391
						break;
392 8590 slaughter
					objsToSync = IdentifierManager
393
							.getInstance()
394
							.querySystemMetadata(startTime, endTime,
395 9249 tao
									objectFormatId, thisMN, start, count, id, isSid);
396 8590 slaughter
				} catch (Exception e) {
397
					logMetacat.error("Error syncing ids");
398 8610 slaughter
					syncError = true;
399 8590 slaughter
					break;
400
				}
401
			}
402
			logMetacat
403
					.debug("syncTask thread completed. Number of guids synced: "
404
							+ syncedIds.size());
405 8610 slaughter
			if (!syncError) {
406
				try {
407
					PropertyService.setProperty(
408
							"dataone.syncaccesspolicies.synced",
409
							Boolean.TRUE.toString());
410
				} catch (GeneralPropertyException e) {
411
					logMetacat
412
							.error("Unable to update property dataone.syncaccesspolicies.synced=true");
413
				}
414
			}
415 8590 slaughter
		}
416 8452 slaughter
	}
417
418
	/**
419
	 * Compare two d1 system metadata access policies for equivalence.
420
	 *
421
	 * @param ap1
422
	 *            - first access policy in the comparison
423
	 * @param ap2
424
	 *            - second access policy in the comparison
425 8590 slaughter
	 * @return boolean - true if access policies are equivalent
426 8452 slaughter
	 */
427 8610 slaughter
	public boolean isEqual(AccessPolicy ap1, AccessPolicy ap2) {
428 8452 slaughter
429 8614 leinfelder
		// can't check when either is null
430
		if (ap1 == null || ap2 == null) {
431
			return false;
432
		}
433
434 8491 slaughter
		// Access Policy -> Access Rule -> (Subject, Permission)
435
		// i.e. Subject="slaughter", Permission="read,write,changePermission"
436 8452 slaughter
		// Get the list of access rules for each access policy
437 8614 leinfelder
		List<AccessRule> allowList1 = ap1
438 8452 slaughter
				.getAllowList();
439 8614 leinfelder
		List<AccessRule> allowList2 = ap2
440 8452 slaughter
				.getAllowList();
441
442
		HashMap<Subject, Set<Permission>> userPerms1 = new HashMap<Subject, Set<Permission>>();
443
		HashMap<Subject, Set<Permission>> userPerms2 = new HashMap<Subject, Set<Permission>>();
444
445
		// Load the permissions from the access rules into a hash of sets, i.e.,
446
		// so that we end up with this:
447 8491 slaughter
		// hash key: set of permissions, i.e.
448 8452 slaughter
		// ----------------------------
449
		// user1: read, write
450
		// user2: read
451
		// user3: read, write, change permissions
452
		// With the permissions in this structure, they can be easily compared
453
		Set<Permission> perms = null;
454
		// Process first access policy
455 8491 slaughter
		// Loop through access rules of this allowList
456 8614 leinfelder
		for (AccessRule accessRule : allowList1) {
457 8452 slaughter
			for (Subject s : accessRule.getSubjectList()) {
458
				if (userPerms1.containsKey(s)) {
459
					perms = userPerms1.get(s);
460
				} else {
461
					perms = new HashSet<Permission>();
462
				}
463
				for (Permission p : accessRule.getPermissionList()) {
464
					perms.add(p);
465
				}
466 8491 slaughter
				userPerms1.put(s, perms);
467 8452 slaughter
			}
468
		}
469
470
		// Process second access policy
471 8614 leinfelder
		for (AccessRule accessRule : allowList2) {
472 8452 slaughter
			for (Subject s : accessRule.getSubjectList()) {
473
				if (userPerms2.containsKey(s)) {
474
					perms = userPerms2.get(s);
475
				} else {
476
					perms = new HashSet<Permission>();
477
				}
478
				for (Permission p : accessRule.getPermissionList()) {
479
					perms.add(p);
480
				}
481 8491 slaughter
				userPerms2.put(s, perms);
482 8452 slaughter
			}
483
		}
484
485 8561 slaughter
		// Check if the number of access rules is the same for mn and cn. If not
486 8590 slaughter
		// then consider them not equal, without performing diff of each access
487
		// rule.
488 8561 slaughter
		if (userPerms1.entrySet().size() != userPerms2.entrySet().size())
489
			return false;
490 8590 slaughter
491
		// Now perform the comparison of each access rule of access policy 1 to
492
		// ap 2.
493
		// This test assumes that the mn perms are more complete than the cn
494
		// perms.
495 8491 slaughter
		logMetacat.debug("Performing comparison of access policies");
496 8452 slaughter
		for (Map.Entry<Subject, Set<Permission>> entry : userPerms1.entrySet()) {
497
			// User name
498
			Subject s1 = entry.getKey();
499
			// Perms that the user holds
500 8491 slaughter
			Set<Permission> p1 = entry.getValue();
501 8590 slaughter
			logMetacat
502
					.debug("Checking access policy of user: " + s1.getValue());
503 8452 slaughter
504 8491 slaughter
			// Does this user exist in both access policies?
505 8452 slaughter
			if (userPerms2.containsKey(s1)) {
506 8491 slaughter
				if (!p1.equals(userPerms2.get(s1))) {
507
					logMetacat.debug("User access policies not equal");
508 8452 slaughter
					return false;
509
				}
510
			} else {
511 8491 slaughter
				logMetacat.debug("User access policy not found on CN");
512 8452 slaughter
				return false;
513
			}
514
		}
515
516
		// All comparisons have been passed, so the two access policies are
517
		// equivalent
518 8491 slaughter
		logMetacat.debug("Access policies are the same");
519 8452 slaughter
		return true;
520
	}
521
522 8585 leinfelder
	/**
523 8590 slaughter
	 * Run pid synch script on the given pids Each argument is an individual pid
524
	 * because pids cannot contain whitespace.
525
	 *
526 8585 leinfelder
	 * @param args
527
	 * @throws Exception
528
	 */
529 8452 slaughter
	public static void main(String[] args) throws Exception {
530
531
		// set up the properties based on the test/deployed configuration of the
532
		// workspace
533 8590 slaughter
		SortedProperties testProperties = new SortedProperties(
534
				"test/test.properties");
535 8452 slaughter
		testProperties.load();
536 8590 slaughter
		String metacatContextDir = testProperties
537
				.getProperty("metacat.contextDir");
538 8452 slaughter
		PropertyService.getInstance(metacatContextDir + "/WEB-INF");
539 8590 slaughter
540 8585 leinfelder
		ArrayList<String> guids = null;
541
		SyncAccessPolicy syncAP = new SyncAccessPolicy();
542 8452 slaughter
543
		if (args.length > 0) {
544
			try {
545 8585 leinfelder
				guids = new ArrayList<String>(Arrays.asList(args));
546 8590 slaughter
				logMetacat.warn("Trying to syncing access policy for "
547
						+ args.length + " pids");
548 8585 leinfelder
				List<Identifier> synchedPids = syncAP.sync(guids);
549 8590 slaughter
				logMetacat.warn("Sunk access policies for "
550
						+ synchedPids.size() + " pids");
551 8452 slaughter
			} catch (Exception e) {
552 8590 slaughter
				logMetacat.error(
553
						"Error syncing pids, message: " + e.getMessage(), e);
554 8452 slaughter
				System.exit(1);
555
			}
556
		}
557
	}
558
}